1 Brief description of data set

A library of Rubisco variants based on a mutant variant of Gallionella sp. CbbM was designed. The CbbM variant is two amino acid exchanges away from the wild-type sequence and was identified as part of a small library which was used to establish the screening system. For reasons of simplicity, in the following, “WT” will refer to this variant, which is the “base” of the whole library. The large library consists of 67 positions, which were separately exchanged by any other possible amino acid and a combinatorial part, which is based on seven amino acid positions, which were exchanged, in a combinatorial fashion, by a few specific possible residues per position.

Both parts of the library were designed with the help of EVmutation and DeepSequence, which use phylogenetic information to predict beneficial amino acid exchanges. The exact manner how predictions were performed is described along with the small library the “base” mutant variant is derived from.

The complete library was transformed into a Synechocystis host strain, in which transcription of the endogenous Rubisco transcript can be inhibited by the induction of a CRISPR inhibition system. Each variant is represented by one or several clones, which can be distinguished on base of their N20 barcodes. The pooled library of these strains was grown in 8 replicates at different gas feed and light conditions in turbidostat mode for approximately 10 generations (CL_N2: constant light 300 µE, 5% CO2, 95% N2, 0% O2, CL_O2: constant light 300 µE, 5% CO2, 75% N2, 20% O2, LD: light-dark cycles, 5% CO2, 75% N2, 20% O2). By taking samples for Illumina sequencing regularly, we were able to track almost all variants present and assign fitness values.

Note that amino acid numbering in the following are based on the sequence including the N-Strep tag.

Fitness values are given as normalized values, i.e. a value of 1.0 equals the fitness of the base variant (actually the underlying CbbM variant which is two amino acid exchanges away from wild-type CbbM) and 0.0 equals the median value of K214 variants present in the data set. K214 is the carbamylated lysine which is essential for catalysis. Exchanges of this lysine residue should result in catalytically inactive enzyme. Mutant variants with worse fitness values than K214 substitutions can be considered catalytically inactive.

In a first step, all data of relevance will be loaded.

2 Diagnostic plots for cultivation

2.1 Sample-sample correlation

When clustering according to similarity, replicates from the same gas conditions have a tendency to cluster together, irrespective of their light condition (constant light or light-dark). This is also the case at generation 0 - even at generation 0, samples are more similar to other samples from the same gas feed than to generation 0 samples of the other round of cultivation (first round of cultivation was CL_N2; CL_O2 and LD were run in a second round of cultivations).

There is some clear separation between samples from earlier time points and samples from later time points.

df_counts <- tidyr::pivot_longer(count_matrix,
    cols = 2:ncol(count_matrix),
    names_to = "sample", values_to = "n_reads"
)

# sort
df_counts <- arrange(df_counts, sample)
df_counts <- left_join(df_samplesheet, df_counts)

df_correlation <- df_counts[,c("name", "sgRNA", "n_reads")] %>%
    tidyr::pivot_wider(names_from = "name", values_from = "n_reads") %>%
    dplyr::select(-c(1)) %>%
    cor()
annotation_days = data.frame(row.names=unique(row.names(df_correlation)), generation=as.character(c(rep(c("0", "3.7", "8.6", "10.1"), 2), rep(c("10.1", "8.6", "3.7", "0"), 2), rep(c("3.7", "10.1", "0", "8.6"), 2), "8.6", "0", "10.1", "3.7", "3.7", "0", "0", "5.4", "6.4", "7.7", "10.3", "0", "5.4", "0", "5.4", rep(c("0", "5.4", "6.4", "7.7", "10.3"), 5), rep(c("0", "4.9", "8.6", "12.5"),8))), condition=c(rep("CL_N2", 30), rep("LD", 34), rep("CL_O2", 32)), replicate=as.character(c(rep("1", 4), rep("2", 4), rep("3", 4), rep("4", 4), rep("5", 4), rep("6", 4), rep("7", 4), rep("8", 2), rep("1", 5), rep("2", 2), rep("3", 2), rep("4", 5), rep("5", 5), rep("6", 5), rep("7", 5), rep("8", 5), rep("1", 4), rep("2", 4), rep("3", 4), rep("4", 4), rep("5", 4), rep("6", 4), rep("7", 4), rep("8", 4))), gas_feed=c(rep("N2", 30), rep("O2", 66)))

# https://stackoverflow.com/questions/41628450/r-pheatmap-change-annotation-colors-and-prevent-graphics-window-from-popping-up
# choose gradient of colors for generations
# e.g. Tol from https://davidmathlogic.com/colorblind/#%23D81B60-%231E88E5-%23FFC107-%23004D40
okabe <- c("#f0e442ff", "#e69f00ff", "#d55e00ff", "#cc79a7ff", "#009e73ff", "#56b4e9ff", "#0072b2ff", "#aaaaaaff")
generations <- okabe[c(1, 2, 3, 3, 4, 4, 4, 5, 5, 5)]
# generations "0"    "3.7"  "8.6"  "10.1" "5.4"  "6.4"  "7.7"  "10.3" "4.9"  "12.5"
names(generations) <- c("0", "3.7", "4.9","5.4", "6.4", "7.7", "8.6", "10.1", "10.3", "12.5")
# replicates 1 to 8
rep <- okabe[1:8]
names(rep) <- as.character(1:8)
feed <- okabe[c(2, 7)]
names(feed) <- c("N2", "O2")
annotation_color_list <- list(condition=col_conditions, replicate=rep, generation=generations, gas_feed=feed)

# https://bioinformatics.stackexchange.com/questions/22502/manually-set-range-of-colour-scale-in-pheatmap-in-r
color.divisions <- 100

p <- pheatmap(df_correlation, display_numbers=TRUE, treeheight_col=0, cutree_rows = 6, cutree_cols = 6, annotation_row = annotation_days, annotation_colors = annotation_color_list, breaks = seq(0,1, length.out=(color.divisions + 1)), fontsize=6)

ggsave("../lfcSE_weighted_outputImages/pdf/correlation_samples_clustering_numbers.pdf", plot=p, width=20, height=20)

p <- pheatmap(df_correlation, display_numbers=FALSE, treeheight_col=0, cutree_rows = 6, cutree_cols = 6, annotation_row = annotation_days, annotation_colors = annotation_color_list, breaks = seq(0,1, length.out=(color.divisions + 1)), fontsize=6)
p

ggsave("../lfcSE_weighted_outputImages/png/correlation_samples_clustering.png", plot=p, width=11.5, height=10)
ggsave("../lfcSE_weighted_outputImages/pdf/correlation_samples_clustering.pdf", plot=p, width=11.5, height=10)

2.2 Check equal distribution in time 0 samples

For checking the distribution at time point 0, two t = 0 replicates from each condition were used.

count_matrix_time0 <- as.data.frame(data.frame(sgRNA=count_matrix$sgRNA, LD_1=count_matrix$LD1A1, LD_2=count_matrix$LD1A6, O2_1=count_matrix$O22A1, O2_2=count_matrix$O22A5, N2_1=count_matrix$highN4A1, N2_2=count_matrix$highN4A5))

row.names(count_matrix_time0) <- count_matrix_time0$sgRNA
count_matrix_time0$sgRNA <- NULL

design_matrix <- data.frame(group=rep("t0", 6))
row.names(design_matrix) = names(count_matrix_time0)

DESeq2 is used to normalize among the replicates.

ddstime0 <- DESeqDataSetFromMatrix(
  countData = count_matrix_time0,
  colData = design_matrix,
  design = ~ 1)
ddstime0 <- estimateSizeFactors(ddstime0)
counts_norm_ddstime0 <- as.data.frame(counts(ddstime0, normalized=TRUE))

When log10-transforming the x-scale of the mean of counts in the different samples, the distribution looks as if mean values were almost normally distributed. This is, if I am not mistaken, relatively typical for count data. (Count data does usually follow a Poisson distribution, which looks more “normal” when being log-transformed)

df_plot <- data.frame(mut=row.names(counts_norm_ddstime0), mean_count=apply(counts_norm_ddstime0, 1, mean))
p <- ggplot(df_plot, aes(x=mean_count)) + geom_histogram() + theme_light() + scale_x_log10()
p

The plot below shows mean and stdev of the count of 100 randomly picked barcodes present in the samples at time point 0 and confirms the histogram from above. There are outliers with many counts or almost no counts and a huge bulk of barcodes with a similar count. According to the histogram above, this bulk is approx. at a mean count of 10.

df_plot <- counts_norm_ddstime0[round(runif(n=100, min=1, max=nrow(counts_norm_ddstime0))),]
df_plot <- data.frame(mut=row.names(df_plot), mean_count=apply(df_plot, 1, mean), stdev=apply(df_plot,1,sd))
p <- ggplot(df_plot) + geom_bar(aes(x=reorder(mut, mean_count), y=mean_count), stat="identity") + geom_errorbar(aes(x=mut, ymin=mean_count-stdev, ymax=mean_count+stdev), width=0.4) + theme_light() + xlab("100 randomly picked barcodes") + ylab("Mean of normalized read counts") + labs(title="Randomly picked barcodes and their average abundance") + theme(axis.text.x=element_blank())
p
ggsave("../lfcSE_weighted_outputImages/pdf/time0_distribution.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/time0_distribution.png", plot=p)

Calculate the Gini index of a few of the samples, a value of 0 reflects complete equality, a value of 1 maximal inequality. Gini indices for all samples are also calculated as part of the whole nf-crispriscreen pipeline. With values of approx. 0.55 to 0.6, barcodes at time point 0 are not completely inequally distributed, but also far from perfect equality.

Gini(counts_norm_ddstime0$LD_1, unbiased=FALSE)
[1] 0.5701114
Gini(counts_norm_ddstime0$LD_2, unbiased=FALSE)
[1] 0.5651001
Gini(counts_norm_ddstime0$O2_1, unbiased=FALSE)
[1] 0.5664372
Gini(counts_norm_ddstime0$O2_2, unbiased=FALSE)
[1] 0.588848
Gini(counts_norm_ddstime0$N2_1, unbiased=FALSE)
[1] 0.5679795
Gini(counts_norm_ddstime0$N2_2, unbiased=FALSE)
[1] 0.5704703
Gini(df_plot$mean_count, unbiased=FALSE)
[1] 0.5540446

3 Exploratory data analysis

3.1 Overview fitness distribution

Normalization: WT is set to “1”, and the median of K214 substitutions as “0”. For all three conditions, the data set shows a bimodal distribution. This is most pronounced for the continuous light, N2 feed condition.

3.1.1 Complete library

plot_subset <- unique(fitness_data[,c("sgRNA_target", "norm", "p_fit_adj_WT", "condition")])

p <- ggplot(plot_subset, aes(x=norm, group=condition, fill=condition, color=condition, linetype=condition)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_fill_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_color_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_linetype_manual(values=c("CL_N2"="solid", "CL_O2"="dashed", "LD"="dotted"), labels=c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + labs(title="Complete CbbM library", x="Normalized fitness value") 
p
ggsave("../lfcSE_weighted_outputImages/pdf/whole_library_densityplot.pdf", plot=p, width=11.5, height=8)
ggsave("../lfcSE_weighted_outputImages/png/whole_library_densityplot.png", plot=p, width=11.5, height=8)

for(cond in unique(plot_subset$condition)){
  print(cond)
  temp_subs <- subset(plot_subset, plot_subset$condition==cond)
  print(nrow(temp_subs))
  print("Above 1")
  print(nrow(subset(temp_subs, temp_subs$norm > 1)))
  print("Above 1, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1))/nrow(temp_subs))
  print("Above 1 and significantly higher than WT")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05)))
  print("Above 1 and significantly higher than WT, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05))/nrow(temp_subs))
  print("Below 0")
  print(nrow(subset(temp_subs, temp_subs$norm < 0)))
  print("Below 0, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm < 0))/nrow(temp_subs))
}
[1] "CL_N2"
[1] 13966
[1] "Above 1"
[1] 1351
[1] "Above 1, percentage"
[1] 0.09673493
[1] "Above 1 and significantly higher than WT"
[1] 180
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.01288844
[1] "Below 0"
[1] 159
[1] "Below 0, percentage"
[1] 0.01138479
[1] "CL_O2"
[1] 13966
[1] "Above 1"
[1] 1584
[1] "Above 1, percentage"
[1] 0.1134183
[1] "Above 1 and significantly higher than WT"
[1] 280
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.02004869
[1] "Below 0"
[1] 1655
[1] "Below 0, percentage"
[1] 0.1185021
[1] "LD"
[1] 13966
[1] "Above 1"
[1] 582
[1] "Above 1, percentage"
[1] 0.04167263
[1] "Above 1 and significantly higher than WT"
[1] 42
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.003007303
[1] "Below 0"
[1] 691
[1] "Below 0, percentage"
[1] 0.0494773

3.1.2 Combinatorial library

When only checking mutant variants present in the combinatorial library with more than a single amino acid exchange, we still observe a clear bimodal distribution very similar to the one of the complete data set.

plot_subset <- unique(subset(fitness_data, (fitness_data$category %in% c("combinatorial")) & fitness_data$number_muts > 1)[,c("sgRNA_target", "norm", "p_fit_adj_WT", "condition")])
length(unique(plot_subset$sgRNA_target))
[1] 12708
length(unique(plot_subset$sgRNA_target))/length(unique(fitness_data$sgRNA_target))
[1] 0.9099241
p <- ggplot(plot_subset, aes(x=norm, group=condition, fill=condition, color=condition, linetype=condition)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_linetype_manual(values=c("CL_N2"="solid", "CL_O2"="dashed", "LD"="dotted"), labels=c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_fill_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_color_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + labs(title="Combinatorial CbbM library with more than one exchange", x="Normalized fitness value")
p
ggsave("../lfcSE_weighted_outputImages/pdf/combinatorial_w-oSingles_library_densityplot.pdf", plot=p, width=11.5, height=8)
ggsave("../lfcSE_weighted_outputImages/pdf/combinatorial_w-oSingles_library_densityplot.png", plot=p, width=11.5, height=8)

for(cond in unique(plot_subset$condition)){
  print(cond)
  temp_subs <- subset(plot_subset, plot_subset$condition==cond)
  print(nrow(temp_subs))
  print("Above 1")
  print(nrow(subset(temp_subs, temp_subs$norm > 1)))
  print("Above 1, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1))/nrow(temp_subs))
  print("Above 1 and significantly higher than WT")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05)))
  print("Above 1 and significantly higher than WT, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05))/nrow(temp_subs))
  print("Below 0")
  print(nrow(subset(temp_subs, temp_subs$norm < 0)))
  print("Below 0, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm < 0))/nrow(temp_subs))
}
[1] "CL_N2"
[1] 12708
[1] "Above 1"
[1] 1059
[1] "Above 1, percentage"
[1] 0.08333333
[1] "Above 1 and significantly higher than WT"
[1] 93
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.007318225
[1] "Below 0"
[1] 106
[1] "Below 0, percentage"
[1] 0.008341202
[1] "CL_O2"
[1] 12708
[1] "Above 1"
[1] 1248
[1] "Above 1, percentage"
[1] 0.09820585
[1] "Above 1 and significantly higher than WT"
[1] 144
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.01133144
[1] "Below 0"
[1] 1526
[1] "Below 0, percentage"
[1] 0.1200818
[1] "LD"
[1] 12708
[1] "Above 1"
[1] 352
[1] "Above 1, percentage"
[1] 0.02769909
[1] "Above 1 and significantly higher than WT"
[1] 3
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.0002360718
[1] "Below 0"
[1] 614
[1] "Below 0, percentage"
[1] 0.04831602

3.1.3 Saturational library

plot_subset <- unique(subset(fitness_data, fitness_data$category %in% c("saturational", "combiANDsatur"))[,c("sgRNA_target", "norm", "p_fit_adj_WT", "num_barcodes", "condition")])
length(unique(plot_subset$sgRNA_target))
[1] 1223
length(unique(plot_subset$sgRNA_target))/length(unique(fitness_data$sgRNA_target))
[1] 0.08756981
p <- ggplot(plot_subset, aes(x=norm, group=condition, fill=condition, color=condition, linetype=condition)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_fill_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_color_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + labs(title="Saturational CbbM library", x="Normalized fitness value") + scale_linetype_manual(values=c("CL_N2"="solid", "CL_O2"="dashed", "LD"="dotted"), labels=c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed"))
p
ggsave("../lfcSE_weighted_outputImages/pdf/saturational_library_densityplot.pdf", plot=p, width=11.5, height=8)
ggsave("../lfcSE_weighted_outputImages/pdf/saturational_library_densityplot.png", plot=p, width=11.5, height=8)

for(cond in unique(plot_subset$condition)){
  print(cond)
  temp_subs <- subset(plot_subset, plot_subset$condition==cond)
  print(nrow(temp_subs))
  print("Above 1")
  print(nrow(subset(temp_subs, temp_subs$norm > 1)))
  print("Above 1, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1))/nrow(temp_subs))
  print("Above 1 and significantly higher than WT")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05)))
  print("Above 1 and significantly higher than WT, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05))/nrow(temp_subs))
  print("Below 0")
  print(nrow(subset(temp_subs, temp_subs$norm < 0)))
  print("Below 0, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm < 0))/nrow(temp_subs))
}
[1] "CL_N2"
[1] 1223
[1] "Above 1"
[1] 291
[1] "Above 1, percentage"
[1] 0.2379395
[1] "Above 1 and significantly higher than WT"
[1] 87
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.07113655
[1] "Below 0"
[1] 42
[1] "Below 0, percentage"
[1] 0.03434178
[1] "CL_O2"
[1] 1223
[1] "Above 1"
[1] 333
[1] "Above 1, percentage"
[1] 0.2722813
[1] "Above 1 and significantly higher than WT"
[1] 135
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.1103843
[1] "Below 0"
[1] 106
[1] "Below 0, percentage"
[1] 0.08667212
[1] "LD"
[1] 1223
[1] "Above 1"
[1] 230
[1] "Above 1, percentage"
[1] 0.1880621
[1] "Above 1 and significantly higher than WT"
[1] 39
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.0318888
[1] "Below 0"
[1] 63
[1] "Below 0, percentage"
[1] 0.05151267

3.1.4 Comparison combinatorial and saturational library

When comparing fitness values between the combinatorial and saturational part directly, it becomes obvious that the saturational part contains, relatively speaking, more variants with functional Rubisco variants, i.e. the right peak of the bimodal distribution is higher than the left peak contrasting to the case in the combinatorial part of the library.

fitness_data_2 <- subset(fitness_data, fitness_data$category!="notExpected" & fitness_data$category!="WT")
fitness_data_2[fitness_data_2$category=="combiANDsatur",]$category <- "saturational"
fitness_data_2 <- unique(fitness_data_2[,c("sgRNA_target", "norm", "p_fit_adj_WT", "category", "condition")])
p <- ggplot(fitness_data_2, aes(x=norm, fill=category, color=category, linetype=category)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_fill_manual(values=c(col_combi_sat), labels = c("Combinatorial", "Saturational")) + scale_color_manual(values=c(col_combi_sat), labels = c("Combinatorial", "Saturational")) + labs(x="Normalized fitness value") + scale_linetype_manual(values=c("combinatorial"="solid", "saturational"="dashed"), labels=c("Combinatorial", "Saturational")) + facet_wrap(~condition) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/density_compareCombiSatur.pdf", plot=p, width=10, height=4, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/density_compareCombiSatur.png", plot=p)

3.2 Overview growth all variants

The mean of the log2FC of each variant present in the library is shown. Pink shows K214H, which is under all three conditions the K214 variant performing best, black the WT variant. Purple shows K214D and K214I, the two K214 variants performing worst. There is a clear separation of different variants, WT is clearly gaining in relative abundance and the investigated K214 variants are dropping. This confirms successful selection according to Rubisco activity.

fit_factor <- fitness_data
fit_factor$sgRNA_target <- factor(fit_factor$sgRNA_target, levels=c(unique(subset(fit_factor, !fit_factor$sgRNA_target %in% c("WT", "K214R"))$sgRNA_target), "WT", "K214R"))
p <- ggplot(fit_factor, aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target, color=sgRNA_target, linewidth=sgRNA_target, alpha=sgRNA_target)) + geom_line() + scale_color_manual(values=c("K214R"="purple", "WT"="black"), na.value="lightgray") + theme_light() + xlab("Time (generations)") + ylab("log2FC(to g=0)") + scale_discrete_manual("linewidth", values=c("K214R"=0.3, "WT"=0.3), na.value=0.1) + scale_alpha_manual(values=c("K214R"=1, "WT"=1), na.value=0.5) + facet_wrap(~condition) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title=element_blank())
p
ggsave("../lfcSE_weighted_outputImages/pdf/all_sgRNAtargets_timeLinePlot.pdf", plot=p, height=1.5, width=5, units="in")
ggsave("../lfcSE_weighted_outputImages/png/all_sgRNAtargets_timeLinePlot.png", plot=p, height=1.5, width=5, units="in")

3.3 Plot WT and K214 variants

The 95% confidence interval for the weighted mean log2FC of all WT barcodes is relatively small, meaning that we can rely relatively well on the respective data. In total, 30 different barcodes for WT were taken into account. These were the highest 30 barcodes assigned to the WT variant present in the complete data set. The high number of WT barcodes as well as their high count numbers both contribute to a reliable estimate of the corresponding fitness.

WT_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target=="WT")[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition")])
p <- lineplot_CVinterval_severalColours_meanlog2(WT_subset) + labs(title="WT with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_timeLinePlot.png", plot=p)

Here, the log2FC charts of the different barcodes used for determining WT data are plotted. 95% confidence intervals are colored according to the standard error of the log2FC - the more transparent, the higher the standard error and the other way round. The barcodes deviating from the overall standard have higher standard errors than the barcodes which are close to the overall mean. In black, the overall trend is added.

It seems as if most of the 30 barcodes agreed well. Note that, for continuous light and an O2-containing feed, several barcodes go a bit down for the last time point. It seems as if selection was already lost then or at least got much weaker.

p <- lineplot_CVinterval_severalColours_log2(WT_subset) + labs(title="WT barcodes with 95% CI") + geom_line(aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target), color="black")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_barcodes_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_barcodes_timeLinePlot.png", plot=p)


lineplot_severalColours_log2(WT_subset) + labs(title="WT barcodes") + geom_line(aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target), color="black")

Here, all K214 mutant variants and their weighted mean log2FoldChange are plotted. All have a tendency to decrease in relative abundance. For continuous light and an O2-containing gas feed, the last time point looks as if selection got worse. Same also holds, to some degree, for the N2-containing feed. K214H goes pretty wild in the LD condition.

K214_subset <- unique(subset(fitness_data, grepl("K214", fitness_data$sgRNA_target))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition", "norm")])
p <- lineplot_CVinterval_severalColours_meanlog2(K214_subset) + labs(title="K214 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K214_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K214_variants_timeLinePlot.png", plot=p)


lineplot_severalColours_meanlog2(K214_subset) + labs(title="K214 variants")

K214H is the K214 variant with the highest fitness score under all three investigated conditions. Its fitness value was determined on basis of 3 barcodes, of which one shows a slower decrease than the other 2 (in CL_O2 and LD, it does not even show a decrease), but has a lower standard error. In black, the weighted mean log2FC is shown.

K214H_subset <- unique(subset(fitness_data, grepl("K214H", fitness_data$sgRNA_target))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition")])
p <- lineplot_CVinterval_severalColours_log2(K214H_subset) + labs(title="K214H barcodes with 95% CI") + geom_line(aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target), color="black")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K214H_barcodes_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K214H_barcodes_timeLinePlot.png", plot=p)

Compare K214H to WT. In LD, due to the strongly rising barcode, K214H and WT are not significantly different (if we take 95% confidence intervals as measure for “Could it even be a significant difference”).

p <- lineplot_CVinterval_severalColours_meanlog2(bind_rows(WT_subset,K214H_subset)) + labs(title="K214H and WT")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_K214H_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_K214H_timeLinePlot.png", plot=p)

K214 variants K214D (LD), K214R (CL_N2) and K214E (CL_O2) were the ones which were used for median normalization (compare compare_weightingStrategies_2ndRound.html). All look as if they were good negative controls in subsequent plots. K214R is closest to a normalized fitness value of 0 of these three. When calculating the absolute distance to 0 of all variants in the normalized data set, K214L also seems to be a good candidate. Since K214R has a lower Gini index and a comparable absolute distance to 0 in all data sets, K214R will be plotted as negative control subsequently.

K214DRT_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target %in% c("K214D", "K214R", "K214E", "K214L"))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition", "norm")])
p <- lineplot_CVinterval_severalColours_meanlog2(bind_rows(WT_subset,K214DRT_subset)) + labs(title="K214 variants and WT")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_K214DRTL_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_K214DRTL_timeLinePlot.png", plot=p)


pivot_wider(unique(K214DRT_subset[,c("sgRNA_target", "condition", "norm")]), values_from=norm, names_from=condition)

K214_set <- pivot_wider(unique(K214_subset[,c("sgRNA_target", "condition", "norm")]), values_from=norm, names_from=condition)
K214_set$sum <- apply(abs(K214_set[,c(2:4)]), 1, sum)
K214_set$gini <- apply(abs(K214_set[,c(2:4)]), 1, Gini)
K214_set[order(K214_set$sum),]

K214L_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target %in% c("K214L", "K214R"))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition", "norm")])
lineplot_CVinterval_severalColours_meanlog2(bind_rows(WT_subset,K214L_subset)) + labs(title="K214L, K214R and WT")

neg_control_K214 <- "K214R"

All K214 variants and WT

p <- lineplot_severalColours_meanlog2(bind_rows(K214_subset, WT_subset)) + labs(title="K214 variants and WT") + scale_color_manual(values=c("WT"="black"), na.value="lightgray")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_K214_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_K214_variants_timeLinePlot.png", plot=p)

3.4 Barplots number mutations ~ fitness

When investigating all mutant variants present in the library, including the ones which occurred spontaneously, there is a clear trend observable that the more amino acid exchanges/mutations were introduced, the lower the mean fitness. This matches well with what was reported in literature before.

red_fitness <- unique(fitness_data[c("sgRNA_target", "mean_fitness", "condition", "category", "number_muts", "norm", "p_fit_adj_WT", "num_barcodes")])
p <- ggplot(red_fitness, aes(x=number_muts, y=norm, group=number_muts, colour=number_muts, fill=number_muts)) + geom_point(size=0.1, position=position_jitterdodge(dodge.width=0.9), color="#4a4a4aff") + theme_light() + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0, color="#4a4a4aff", fill="#4a4a4aff") + ylab("Weighted mean fitness") + facet_wrap(~condition)+ theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_complete.pdf", p, width=30, height=18, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_complete.png", p, width=30, height=18, units="cm")

When checking the whole “expected” library, i.e. the combination of saturational and combinatorial library, this trend is still observable.

expected_red_fitness <- subset(red_fitness, !red_fitness$category=="notExpected")
p <- ggplot(expected_red_fitness, aes(x=number_muts, y=norm, fill=number_muts, colour=number_muts, group=number_muts)) + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0, color="#4a4a4aff", fill="#4a4a4aff") + geom_point(size=0.05, alpha=0.7, position=position_jitterdodge(dodge.width=0.9), color="#4a4a4aff") + theme_light() + ylab("Normalized fitness score") + xlab("Number of intoduced amino acid exchanges") + theme(legend.position="none") + facet_wrap(~condition) + coord_cartesian(xlim=c(-0.2,7.2))+ theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_onlyExpected.pdf", p, width=5, height=2.4, units="in")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_onlyExpected.png", p, width=5, height=3, units="in")

When only comparing the amino acid exchanges present in the combinatorial library, the overall trend that introducing another amino acid exchange decreases the fitness value becomes even clearer. At the same time, additionally introduced exchanges do not reduce the maximum fitness value.

combinatorial_wide <- subset(expected_red_fitness, !expected_red_fitness$category=="saturational")
lm_fitness_data <- lm(norm ~ number_muts, combinatorial_wide)
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = combinatorial_wide)

Residuals:
     Min       1Q   Median       3Q 
-1.29518 -0.22402 -0.04572  0.18624 
     Max 
 2.04364 

Coefficients:
             Estimate Std. Error
(Intercept)  0.921151   0.007505
number_muts -0.096703   0.001414
            t value Pr(>|t|)    
(Intercept)  122.73   <2e-16 ***
number_muts  -68.39   <2e-16 ***
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’
  0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3158 on 38188 degrees of freedom
Multiple R-squared:  0.1091,    Adjusted R-squared:  0.1091 
F-statistic:  4677 on 1 and 38188 DF,  p-value: < 2.2e-16
lm_fitness_data <- lm(norm ~ number_muts, subset(combinatorial_wide, combinatorial_wide$condition == "CL_N2"))
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = subset(combinatorial_wide, 
    combinatorial_wide$condition == "CL_N2"))

Residuals:
     Min       1Q   Median       3Q 
-0.81547 -0.20798 -0.04122  0.18888 
     Max 
 1.16395 

Coefficients:
             Estimate Std. Error
(Intercept)  0.988449   0.011546
number_muts -0.091316   0.002175
            t value Pr(>|t|)    
(Intercept)   85.61   <2e-16 ***
number_muts  -41.98   <2e-16 ***
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’
  0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2805 on 12728 degrees of freedom
Multiple R-squared:  0.1216,    Adjusted R-squared:  0.1216 
F-statistic:  1762 on 1 and 12728 DF,  p-value: < 2.2e-16
lm_fitness_data <- lm(norm ~ number_muts, subset(combinatorial_wide, combinatorial_wide$condition == "CL_O2"))
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = subset(combinatorial_wide, 
    combinatorial_wide$condition == "CL_O2"))

Residuals:
     Min       1Q   Median       3Q 
-1.00643 -0.26879 -0.08333  0.21430 
     Max 
 2.11737 

Coefficients:
             Estimate Std. Error
(Intercept)  0.968194   0.015364
number_muts -0.113957   0.002895
            t value Pr(>|t|)    
(Intercept)   63.02   <2e-16 ***
number_muts  -39.37   <2e-16 ***
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’
  0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3733 on 12728 degrees of freedom
Multiple R-squared:  0.1086,    Adjusted R-squared:  0.1085 
F-statistic:  1550 on 1 and 12728 DF,  p-value: < 2.2e-16
lm_fitness_data <- lm(norm ~ number_muts, subset(combinatorial_wide, combinatorial_wide$condition == "LD"))
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = subset(combinatorial_wide, 
    combinatorial_wide$condition == "LD"))

Residuals:
     Min       1Q   Median       3Q 
-1.24018 -0.17207 -0.03075  0.14309 
     Max 
 1.66143 

Coefficients:
             Estimate Std. Error
(Intercept)  0.806809   0.010654
number_muts -0.084837   0.002007
            t value Pr(>|t|)    
(Intercept)   75.73   <2e-16 ***
number_muts  -42.27   <2e-16 ***
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’
  0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2588 on 12728 degrees of freedom
Multiple R-squared:  0.1231,    Adjusted R-squared:  0.123 
F-statistic:  1786 on 1 and 12728 DF,  p-value: < 2.2e-16
p <- ggplot(combinatorial_wide, aes(x=number_muts, y=norm, fill=number_muts, colour=number_muts)) + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0, color="#4a4a4aff", fill="#4a4a4aff") + geom_point(size=0.1, position=position_jitterdodge(dodge.width=0.9), color="#4a4a4aff") + theme_light() + ylab("Normalized fitness score")+ theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + facet_wrap(~condition) + coord_cartesian(xlim=c(-0.2,7.2))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_onlyCombi.pdf", p, width=30, height=18, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_onlyCombi.png", p, width=30, height=18, units="cm")

When only checking variants with fitness values significantly different to the “wild-type” sequence, this pattern gets dampened, even though there is still some trend in this direction observable with more and more variants showing relatively high scores.

combinatorial_wide <- subset(combinatorial_wide, combinatorial_wide$p_fit_adj_WT<0.05)

p <- ggplot(combinatorial_wide, aes(x=number_muts, y=norm, fill=number_muts, colour=number_muts)) + geom_point(size=0.1, position=position_jitterdodge(dodge.width=0.9)) + theme_light() + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0) + ylab("Weighted mean fitness") + facet_wrap(~condition) + coord_cartesian(xlim=c(0.8,7.2))+ theme(panel.grid.minor =element_blank(), legend.title = element_blank())+ theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_onlyCombi_onlySignificant.pdf", p, width=18, height=18, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_onlyCombi_onlySignificant.png", p, width=18, height=18, units="cm")

3.5 Correlation with different zero-shot predictors and similar

There are different ways of predicting the effect of higher order mutant variants. For designing the library, no prior knowledge was available. For this reason, zero-shot predictions using EVmutation and DeepSequence were performed. When data for single-site mutants is available, an additive model can be used that is assuming independence of different amino acid exchanges. Predictions of this model were calculated using python.

combi_sat_alpha <- c(combinatorial=0.2, saturational=0.2, WT=1.0)
combi_sat_shape <- c(combinatorial=16, saturational=16, WT=4)
combi_sat_size <- c(combinatorial=0.4, saturational=0.4, WT=0.8)
dotsize <- 0.02
pointshape <- 16
alphalevel <- 0.4
line_width_for_plots <- 0.4
red_fitness <- unique(fitness_data[c("sgRNA_target", "norm", "condition", "category", "EVcoup_predict", "DeepSeq_predict", "MSA_Transform", "additive_score", "proteinNPT_predict")])
red_fitness[red_fitness$category=="combiANDsatur",]$category <- "saturational"
red_fitness <- subset(red_fitness, red_fitness$category != "notExpected")
red_fitness[red_fitness$category=="WT",]$EVcoup_predict <- 0

3.5.1 Comparison EVmutation, DeepSequence

red_fitness_noNA_DeepSeq <- subset(red_fitness, !is.na(red_fitness$DeepSeq_predict))

for(cat in c("combinatorial", "saturational")){
  print(cat)
  subset_forLoop <- subset(red_fitness_noNA_DeepSeq, red_fitness_noNA_DeepSeq$category==cat & red_fitness_noNA_DeepSeq$condition=="CL_N2")
  lm <- lm(DeepSeq_predict~EVcoup_predict, subset_forLoop)
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$DeepSeq_predict, method = 'spearman')
  print(correlation)
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$DeepSeq_predict, method = 'pearson')
  print(correlation)
}
[1] "combinatorial"

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
S = 1.4917e+11, p-value <
2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.5638764 


    Pearson's product-moment
    correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
t = 81.392, df = 12706, p-value
< 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5738608 0.5967197
sample estimates:
      cor 
0.5854065 

[1] "saturational"

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
S = 37504520, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8137113 


    Pearson's product-moment
    correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
t = 43.657, df = 1063, p-value
< 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7786249 0.8217409
sample estimates:
      cor 
0.8012205 
scat <- ggplot(red_fitness_noNA_DeepSeq, aes(x=EVcoup_predict, y=DeepSeq_predict, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("EVcouplings fitness prediction") + ylab("DeepSequence fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank())
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_DeepSeq_EVcoup.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_DeepSeq_EVcoup.png", scat, width=2, height=2, units="in")

3.5.2 Comparison EVmutation, MSA Transformer

for(cat in c("combinatorial", "saturational")){
  print(cat)
  subset_forLoop <- subset(red_fitness_noNA_DeepSeq, red_fitness_noNA_DeepSeq$category==cat & red_fitness_noNA_DeepSeq$condition=="CL_N2")
  print(nrow(subset_forLoop))
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$MSA_Transform, method = 'spearman')
  print(correlation)
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$MSA_Transform, method = 'pearson')
  print(correlation)
}
[1] "combinatorial"
[1] 12708

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
S = 1.476e+11, p-value <
2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
     rho 
0.568465 


    Pearson's product-moment
    correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
t = 82.411, df = 12706, p-value
< 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5787463 0.6014095
sample estimates:
      cor 
0.5901942 

[1] "saturational"
[1] 1065

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
S = 55404058, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.7248026 


    Pearson's product-moment
    correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
t = 34.666, df = 1063, p-value
< 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6989539 0.7554535
sample estimates:
      cor 
0.7284399 
scat <- ggplot(red_fitness_noNA_DeepSeq, aes(x=EVcoup_predict, y=MSA_Transform, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("EVcouplings fitness prediction") + ylab("MSA Transform fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank())
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_EVcoup.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_EVcoup.png", scat, width=2, height=2, units="in")

3.5.3 Comparison “additive score” and normalized fitness

3.5.3.1 Pearson

red_fitness_noNA_additive <- subset(red_fitness, !is.na(red_fitness$additive_score))
# https://stackoverflow.com/questions/19699858/ggplot-adding-regression-line-equation-and-r2-with-facet
lm_eqn = function(df){
    m = lm(additive_score ~ norm, df);
    cor_form = cor.test(df$norm, df$additive_score, method = 'pearson')
    eq <- substitute("r ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}

eq <- ddply(red_fitness_noNA_additive,.(condition),lm_eqn)

p <- ggplot(data = red_fitness_noNA_additive, aes(x = norm, y = additive_score, color=category, alpha=category)) +
            geom_point(aes(size=category), shape=pointshape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x,linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Additive score") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_size_manual(values=combi_sat_size) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat)
scat = p + geom_text(data=eq,aes(x = -0.4, y = 1.8,label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_additive_combi.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_additive_combi.png", scat, width=6.8, height=2, units="in")

3.5.3.2 Spearman

red_fitness_noNA_additive <- subset(red_fitness, !is.na(red_fitness$additive_score))
# https://stackoverflow.com/questions/19699858/ggplot-adding-regression-line-equation-and-r2-with-facet
lm_eqn = function(df){
    m = lm(additive_score ~ norm, df);
    cor_form = cor.test(df$norm, df$additive_score, method = 'spearman')
    eq <- substitute("rho ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}

eq <- ddply(red_fitness_noNA_additive,.(condition),lm_eqn)

p <- ggplot(data = red_fitness_noNA_additive, aes(x = norm, y = additive_score, color=category, alpha=category)) +
            geom_point(aes(size=category), shape=pointshape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x,linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Additive score") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_size_manual(values=combi_sat_size) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat)
scat = p + geom_text(data=eq,aes(x = -0.4, y = 1.8,label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_additive_combi_Spearman.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_additive_combi_Spearman.png", scat, width=6.8, height=2, units="in")

3.5.4 Comparison DeepSequence, normalized fitness

lm_eqn = function(df){
    m = lm(DeepSeq_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$DeepSeq_predict, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
red_fitness_noNA_DeepSeq$category2 <- red_fitness_noNA_DeepSeq$category
red_fitness_noNA_DeepSeq[red_fitness_noNA_DeepSeq$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_DeepSeq,.(condition, category2),lm_eqn)

red_fitness_noNA_DeepSeq_onlyComb_no4337 <- subset(red_fitness_noNA_DeepSeq, red_fitness_noNA_DeepSeq$category=="combinatorial" & !grepl("V337", red_fitness_noNA_DeepSeq$sgRNA_target))
correlation <- cor.test(red_fitness_noNA_DeepSeq_onlyComb_no4337$norm, red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict, method = 'spearman')
correlation

    Spearman's rank correlation rho

data:  red_fitness_noNA_DeepSeq_onlyComb_no4337$norm and red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict
S = 3.6157e+11, p-value <
2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
       rho 
-0.1088873 
correlation <- cor.test(red_fitness_noNA_DeepSeq_onlyComb_no4337$norm, red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict, method = 'pearson')
correlation

    Pearson's product-moment
    correlation

data:  red_fitness_noNA_DeepSeq_onlyComb_no4337$norm and red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict
t = -12.552, df = 12505,
p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.12882233 -0.09420665
sample estimates:
       cor 
-0.1115483 
scat <- ggplot(data = red_fitness_noNA_DeepSeq, aes(x = norm, y = DeepSeq_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("DeepSequence prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-15, label=V1), vjust=c(0,1.2,0,1.2,0,1.2), parse = TRUE, inherit.aes=FALSE, size=8, size.unit="pt") + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_DeepSeq_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_DeepSeq_norm.png", scat, width=6.8, height=2, units="in")


red_fitness_noNA_DeepSeq_onlyComb_no4337$category2 <- red_fitness_noNA_DeepSeq_onlyComb_no4337$category
red_fitness_noNA_DeepSeq_onlyComb_no4337[red_fitness_noNA_DeepSeq_onlyComb_no4337$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_DeepSeq_onlyComb_no4337,.(condition, category2),lm_eqn)

scat <- ggplot(data = red_fitness_noNA_DeepSeq_onlyComb_no4337, aes(x = norm, y = DeepSeq_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("DeepSequence prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-15, label=V1), parse = TRUE, inherit.aes=FALSE, size=8, size.unit="pt") + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_DeepSeq_norm_noV323.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_DeepSeq_norm_noV323.png", scat, width=6.8, height=2, units="in")

3.5.5 Comparison EVmutation, normalized fitness

red_fitness_noNA_EVcoup <- subset(red_fitness, !is.na(red_fitness$EVcoup_predict))

lm_eqn = function(df){
    m = lm(EVcoup_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$EVcoup_predict, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
red_fitness_noNA_EVcoup$category2 <- red_fitness_noNA_EVcoup$category
red_fitness_noNA_EVcoup[red_fitness_noNA_EVcoup$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_EVcoup,.(condition, category2),lm_eqn)

red_fitness_noNA_EVcoup_no4337 <- subset(red_fitness_noNA_EVcoup, red_fitness_noNA_EVcoup$category=="combinatorial" & !grepl("V337", red_fitness_noNA_EVcoup$sgRNA_target))
correlation <- cor.test(red_fitness_noNA_EVcoup_no4337$norm, red_fitness_noNA_EVcoup_no4337$EVcoup_predict, method = 'spearman')
correlation

    Spearman's rank correlation rho

data:  red_fitness_noNA_EVcoup_no4337$norm and red_fitness_noNA_EVcoup_no4337$EVcoup_predict
S = 2.5679e+11, p-value <
2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.2124506 
correlation <- cor.test(red_fitness_noNA_EVcoup_no4337$norm, red_fitness_noNA_EVcoup_no4337$EVcoup_predict, method = 'pearson')
correlation

    Pearson's product-moment
    correlation

data:  red_fitness_noNA_EVcoup_no4337$norm and red_fitness_noNA_EVcoup_no4337$EVcoup_predict
t = 23.733, df = 12505, p-value
< 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.1907747 0.2243161
sample estimates:
      cor 
0.2076064 
scat <- ggplot(data = red_fitness_noNA_EVcoup, aes(x = norm, y = EVcoup_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("EVmutation prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-10, label=V1), vjust=c(0,1.2,0,1.2,0,1.2), parse = TRUE, inherit.aes=FALSE, size=8, size.unit = "pt") + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_norm_all_EVcoup.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_norm_all_EVcoup.png", scat, width=6.8, height=2, units="in")


red_fitness_noNA_EVcoup_no4337$category2 <- red_fitness_noNA_EVcoup_no4337$category
red_fitness_noNA_EVcoup_no4337[red_fitness_noNA_EVcoup_no4337$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_EVcoup_no4337,.(condition, category2),lm_eqn)

scat <- ggplot(data = red_fitness_noNA_EVcoup_no4337, aes(x = norm, y = EVcoup_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("EVmutation prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-10, label=V1), parse = TRUE, inherit.aes=FALSE, size=8, size.unit = "pt") + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_norm_all_EVcoup_noV323.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_norm_all_EVcoup_noV323.png", scat, width=6.8, height=2, units="in")

3.5.6 Comparison MSA transformer, normalized fitness

lm_eqn = function(df){
    m = lm(MSA_Transform ~ norm, df);
    cor_form = cor.test(df$norm, df$MSA_Transform, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
red_fitness_noNA_DeepSeq$category2 <- red_fitness_noNA_DeepSeq$category
red_fitness_noNA_DeepSeq[red_fitness_noNA_DeepSeq$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_DeepSeq,.(condition, category2),lm_eqn)

scat <- ggplot(data = red_fitness_noNA_DeepSeq, aes(x = norm, y = MSA_Transform, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("MSATransformer (ensemble) prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-10, label=V1), size=8, size.unit="pt", vjust=c(0,1.2,0,1.2,0,1.2), parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_norm.png", scat, width=6.8, height=2, units="in")


red_fitness_noNA_DeepSeq_onlyComb_no4337$category2 <- red_fitness_noNA_DeepSeq_onlyComb_no4337$category
red_fitness_noNA_DeepSeq_onlyComb_no4337[red_fitness_noNA_DeepSeq_onlyComb_no4337$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_DeepSeq_onlyComb_no4337,.(condition, category2),lm_eqn)

scat <- ggplot(data = red_fitness_noNA_DeepSeq_onlyComb_no4337, aes(x = norm, y = MSA_Transform, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("MSATransformer (ensemble) prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-10, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_norm_noV323.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_norm_noV323.png", scat, width=6.8, height=2, units="in")

3.5.7 Comparison ProteinNPT, normalized fitness

df_proteinNPT <- subset(red_fitness, !is.na(red_fitness$proteinNPT_predict))

lm_eqn = function(df){
    m = lm(proteinNPT_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$proteinNPT_predict, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
df_proteinNPT$category2 <- df_proteinNPT$category
df_proteinNPT[df_proteinNPT$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(df_proteinNPT,.(condition, category2),lm_eqn)

scat <- ggplot(data = df_proteinNPT, aes(x = norm, y = proteinNPT_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("ProteinNPT prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-2, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_proteinNPT_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_proteinNPT_norm.png", scat, width=6.8, height=2, units="in")


df_proteinNPT_noV323 <- subset(df_proteinNPT, !grepl("V337", df_proteinNPT$sgRNA_target))

df_proteinNPT_noV323$category2 <- df_proteinNPT_noV323$category
df_proteinNPT_noV323[df_proteinNPT_noV323$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(df_proteinNPT_noV323,.(condition, category2),lm_eqn)

scat <- ggplot(data = df_proteinNPT_noV323, aes(x = norm, y = proteinNPT_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("ProteinNPT prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-2, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_proteinNPT_norm_noV323.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_proteinNPT_norm_noV323.png", scat, width=6.8, height=2, units="in")



lm_eqn = function(df){
    m = lm(proteinNPT_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$proteinNPT_predict, method = 'spearman')
    eq <- substitute("rho ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
df_proteinNPT$category2 <- df_proteinNPT$category
df_proteinNPT[df_proteinNPT$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(df_proteinNPT,.(condition, category2),lm_eqn)

scat <- ggplot(data = df_proteinNPT, aes(x = norm, y = proteinNPT_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("ProteinNPT prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-2, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_proteinNPT_norm_spearman.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_proteinNPT_norm_spearman.png", scat, width=6.8, height=2, units="in")

3.5.8 Comparison MSA transformer, ProteinNPT

scat <- ggplot(df_proteinNPT, aes(x=proteinNPT_predict, y=MSA_Transform, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("ProteinNPT fitness prediction") + ylab("MSA Transform fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank()) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_proteinNPT.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_proteinNPT.png", scat, width=2, height=2, units="in")


scat <- ggplot(df_proteinNPT, aes(x=proteinNPT_predict, y=additive_score, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("ProteinNPT fitness prediction") + ylab("Additive score fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank()) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_additiveScore_proteinNPT.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_additiveScore_proteinNPT.png", scat, width=2, height=2, units="in")

3.6 Correlation conservation to fitness

cor_subs <- unique(subset(fitness_data, fitness_data$category=="saturational" | fitness_data$category=="combiANDsatur")[,c("aa_pos","norm","conservationScore","category","condition")])
cor_subs[cor_subs$category=="combiANDsatur",]$category <- "saturational"
cor_subs <- subset(cor_subs, !is.na(cor_subs$conservationScore))
cor_subs_2 <- cor_subs %>% dplyr::group_by(aa_pos, condition) %>%
  dplyr::summarize(
    .groups="keep",
    averag_fitness=mean(norm)
  )
cor_subs <- left_join(cor_subs_2,cor_subs[,c("aa_pos","conservationScore","category")])

lm_eqn = function(df){
    m = lm(conservationScore ~ averag_fitness, df);
    cor_form = cor.test(df$averag_fitness, df$conservationScore, method = 'pearson')
    eq <- substitute("Pearson's r ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}
eq <- ddply(cor_subs,.(condition),lm_eqn)

scat <- ggplot(data = cor_subs, aes(x = averag_fitness, y = conservationScore, color=category, alpha=category, linetype=category)) +
            geom_point(aes(size=category, shape=category))  + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Conservation score") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1, y =0.9, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat

ggsave("../lfcSE_weighted_outputImages/pdf/conservation_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/conservation_norm.png", scat, width=6.8, height=2, units="in")

3.7 Correlation surface exposure to fitness

dotsize_surface <- 0.4
surface <- unique(subset(fitness_data, fitness_data$category=="saturational" | fitness_data$category=="combiANDsatur")[,c("aa_pos","norm","relSAS", "dimer","category","condition")])
surface[surface$category=="combiANDsatur",]$category <- "saturational"
surface <- subset(surface, !is.na(surface$relSAS))
surface_2 <- surface %>% dplyr::group_by(aa_pos, condition) %>%
  dplyr::summarize(
    .groups="keep",
    averag_fitness=mean(norm)
  )
surface <- left_join(surface_2,surface[,c("aa_pos","relSAS", "dimer","category")])

lm_eqn = function(df){
    m = lm(relSAS ~ averag_fitness, df);
    cor_form = cor.test(df$averag_fitness, df$relSAS, method = 'pearson')
    eq <- substitute("Pearson's r ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}
eq <- ddply(surface,.(condition),lm_eqn)

scat <- ggplot(data = surface, aes(x = averag_fitness, y = relSAS, color=dimer)) +
            geom_point(size=dotsize_surface, shape=pointshape, alpha=alphalevel) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Relative solvent accessibility") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_color_manual(values=c("#93dfffff"), na.value="#777777") + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1.0, y =-0.05, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/relSAS_norm.pdf", scat, width=7, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/relSAS_norm.png", scat, width=30, height=18, units="cm")



surface_noDimer <- subset(surface, is.na(surface$dimer))
eq <- ddply(surface_noDimer,.(condition),lm_eqn)

scat <- ggplot(data = surface_noDimer, aes(x = averag_fitness, y = relSAS, color=dimer)) +
            geom_point(size=dotsize_surface, shape=pointshape, alpha=alphalevel) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Relative solvent accessibility") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_color_manual(values=c("#93dfffff"), na.value="#777777") + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1.0, y =-0.05, label=V1), size=8, size.unit = "pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/relSAS_norm_noDimer.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/relSAS_norm_noDimer.png", scat, width=6.8, height=2, units="in")



surface_Dimer <- subset(surface, !is.na(surface$dimer))
eq <- ddply(surface_Dimer,.(condition),lm_eqn)

scat <- ggplot(data = surface_Dimer, aes(x = averag_fitness, y = relSAS, color=dimer)) +
            geom_point(size=dotsize_surface, shape=pointshape, alpha=alphalevel) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Relative solvent accessibility") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_color_manual(values=c("#93dfffff"), na.value="#777777") + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1.0, y =-0.05, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/relSAS_norm_onlyDimer.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/relSAS_norm_onlyDimer.png", scat, width=30, height=18, units="cm")

4 Comparing different conditions

A comparison between different conditions helps to identify variants which behave different in different conditions, e.g. because of an enhanced specificity or higher compatibility with light-dark cycles.

4.1 Compare N2 and O2 gas feeds

red_fitness <- unique(fitness_data[,c("sgRNA_target", "norm", "condition", "p_fit_adj_WT", "num_barcodes", "category")])
wide_fitness <- pivot_wider(red_fitness, values_from =c(norm,p_fit_adj_WT), names_from=condition)
wide_fitness$sgRNA_target <- factor(wide_fitness$sgRNA_target, levels=c(unique(subset(wide_fitness, wide_fitness$sgRNA_target != "WT"))$sgRNA_target, "WT"))
wide_fitness$WT_not_WT <- "notWT"
wide_fitness[wide_fitness$sgRNA_target=="WT",]$WT_not_WT <- "WT"

# set some plotting parameters
WT_shape <- c("notWT"=16, "WT"=4)
WT_alpha <- c("notWT"=0.2, "WT"=1.0)
WT_size <- c("notWT"=0.3, "WT"=0.5)
CLO2_CLN2_cutoff <- 0.25
barcode_cutoff <- 2
adjp_cutoff <- 0.05

# extract subsets from big data frames to test if significant differences
subset_signif_highO2 <- subset(fitness_data, fitness_data$sgRNA_target %in% subset(wide_fitness, wide_fitness$norm_CL_O2>1.0 & wide_fitness$p_fit_adj_WT_CL_O2<0.05)$sgRNA_target)
subset_signif_highLD <- subset(fitness_data, fitness_data$sgRNA_target %in% subset(wide_fitness, wide_fitness$norm_LD > wide_fitness$norm_CL_O2 & wide_fitness$norm_LD > 0.9)$sgRNA_target)

# prepare plotting, calculate lm to check distance from it (assuming linear regression gives better 1:1 than line through origin)
lm_CLO2_CLN2 <- lm(norm_CL_N2 ~ norm_CL_O2, wide_fitness)
summary(lm_CLO2_CLN2)

Call:
lm(formula = norm_CL_N2 ~ norm_CL_O2, data = wide_fitness)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.03132 -0.07939  0.01898  0.09884  0.88126 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.266196   0.001775   150.0   <2e-16 ***
norm_CL_O2  0.653940   0.003040   215.1   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1505 on 13964 degrees of freedom
Multiple R-squared:  0.7681,    Adjusted R-squared:  0.7681 
F-statistic: 4.626e+04 on 1 and 13964 DF,  p-value: < 2.2e-16
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_CL_N2, method = 'spearman')
correlation

    Spearman's rank correlation rho

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_CL_N2
S = 7.8282e+10, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8275755 
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_CL_N2, method = 'pearson')
correlation

    Pearson's product-moment correlation

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_CL_N2
t = 215.08, df = 13964, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8725266 0.8802195
sample estimates:
      cor 
0.8764289 
wide_fitness_CL_OL <- wide_fitness
wide_fitness_CL_OL$distance_lm <- wide_fitness_CL_OL$norm_CL_N2 - (lm_CLO2_CLN2$coefficients[1] + lm_CLO2_CLN2$coefficients[2] * wide_fitness_CL_OL$norm_CL_O2)
wide_fitness_CL_OL[order(wide_fitness_CL_OL$distance_lm, decreasing = FALSE),][1:10,]

# coloring according to differential expression
wide_fitness_CL_OL$diff <- "NO"
wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm > 0] <- "CL_N2"
wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm < 0] <- "CL_O2"
#wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm > CLO2_CLN2_cutoff] <- "CL_N2" # in case we care for cut-off for minimal distance from linear regression
#wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm < (-CLO2_CLN2_cutoff)] <- "CL_O2" # in case we care for cut-off for minimal distance from linear regression
wide_fitness_CL_OL[wide_fitness_CL_OL$sgRNA_target=="WT",]$diff <- "WT"
dotplot_colors <- c(col_conditions, "NO"="#d3d3d3b2", "orange", "WT"="black")

# prepare labels for plot
wide_fitness_CL_OL$delabel <- NA
wide_fitness_CL_OL$delabel[wide_fitness_CL_OL$diff !="NO"] <- as.character(wide_fitness_CL_OL$sgRNA_target[wide_fitness_CL_OL$diff != "NO"])

p <- ggplot(wide_fitness_CL_OL, aes(x=norm_CL_O2, y=norm_CL_N2, color=diff, shape=WT_not_WT)) + geom_point(aes(size=WT_not_WT, alpha=WT_not_WT), show.legend=FALSE) + scale_shape_manual(values=WT_shape) + scale_size_manual(values=WT_size) + scale_alpha_manual(values=WT_alpha) + theme_light() + labs(y="Weighted mean fitness value at continuous light, 5% CO2, 95% N2, 0% O2", x="Weighted mean fitness value at continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_CLN2$coefficients[1],slope=lm_CLO2_CLN2$coefficients[2],linetype="dashed",color="black", linewidth=line_width_for_plots) + scale_colour_manual(values = dotplot_colors) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) #+ xlim(-6, +7) +ylim(-6,+7)
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLN2_CLO2_withoutSignif_wo-labels.pdf", plot=p, width=4, height=4, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLN2_CLO2_withoutSignif_wo-labels.png", plot=p, width=4, height=4, units="cm")
p

Use Wilcoxon signed rank exact test to check if barcodes for the same variant behave significiantly different between the two tested conditions.

subset_signif_highO2 <- subset(subset_signif_highO2, subset_signif_highO2$condition %in% c("CL_N2", "CL_O2"))

get_controls <- function(cond_spec, sgRNA_spec){
  control_table <- subset_signif_highO2[subset_signif_highO2$condition != unique(cond_spec) & subset_signif_highO2$sgRNA_target == unique(sgRNA_spec) & subset_signif_highO2$time == 0,]
  control_table$fitness
}

subset_signif_highO2 <- dplyr::left_join(
  subset_signif_highO2,
  subset_signif_highO2 %>%
    dplyr::group_by(sgRNA_target, condition, time) %>%
    dplyr::summarize(
      .groups = "keep",
      # apply Wilcoxon rank sum test against other condition
      p_fitness_condition = stats::wilcox.test(
        x = fitness,
        y = get_controls(condition, sgRNA_target),
        paired = TRUE,
        alternative = "two.sided"
        )$p.value
      ),
  by = c("sgRNA_target", "condition", "time")
  ) 

subset_signif_highO2 <- subset_signif_highO2 %>%
  group_by(condition, time) %>%
  mutate(
    p_fitness_condition_adj = stats::p.adjust(p_fitness_condition, method = "BH")
    )

Plot part of data set which was tested for significant differences and highlight variants which were found to be significant.

subset_signif_highO2_red <- unique(subset_signif_highO2[,c("sgRNA_target", "norm", "condition", "num_barcodes", "p_fitness_condition_adj")])
subset_signif_highO2_red <- pivot_wider(subset_signif_highO2_red, values_from =c(norm,p_fitness_condition_adj), names_from=condition)

# alpha, size and labels according to significance
wide_fitness_CL_OL$signif <- "NO"
wide_fitness_CL_OL$signif[wide_fitness_CL_OL$sgRNA_target %in% unique(subset(subset_signif_highO2, subset_signif_highO2$p_fitness_condition_adj < adjp_cutoff))$sgRNA_target] <- "SIG"
wide_fitness_CL_OL$delabel <- NA
wide_fitness_CL_OL$delabel[wide_fitness_CL_OL$signif =="SIG"] <- as.character(wide_fitness_CL_OL$sgRNA_target[wide_fitness_CL_OL$signif =="SIG"])
wide_fitness_CL_OL[wide_fitness_CL_OL$sgRNA_target=="WT",]$signif <- "WT" # to ensure WT is visible
signif_size <- c("NO"=0.3, "SIG"=0.5, "WT"=0.5)
signif_alpha <- c("NO"=0.2, "SIG"=0.9, "WT"=1.0)

p <- ggplot(wide_fitness_CL_OL, aes(x=norm_CL_O2, y=norm_CL_N2, color=diff, label=delabel, shape=WT_not_WT)) + scale_shape_manual(values=WT_shape) + geom_point(aes(size=signif, alpha=signif), show.legend=FALSE) + scale_size_manual(values=signif_size) + scale_alpha_manual(values=signif_alpha) + theme_light() + labs(y="Weighted mean fitness value at continuous light, 5% CO2, 95% N2, 0% O2", x="Weighted mean fitness value at continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_CLN2$coefficients[1],slope=lm_CLO2_CLN2$coefficients[2],linetype="dashed",color="black", linewidth=line_width_for_plots) + scale_colour_manual(values = dotplot_colors) + geom_text_repel(fontface="italic") + xlim(0.75, 2.5) +ylim(0.75,1.75)
p
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLN2_CLO2_Wilcox_signif.pdf", plot=p, width=12.5, height=12.5, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLN2_CLO2_Wilcox_signif.png", plot=p, width=12.5, height=12.5, units="cm")


wide_fitness_CL_OL[wide_fitness_CL_OL$signif=="SIG",][order(wide_fitness_CL_OL[wide_fitness_CL_OL$signif=="SIG",]$norm_CL_O2, decreasing=TRUE),]
possibly_specific_variants <- c("H358I", "K360I", "M328I", "Y333T", "H358Q", "Y322M", "D329T", "Y333V", "K99E", "M231F", "Y333I", "WT", neg_control_K214)
possibly_specific_variants <- c("H358I", "K360I", "M328I", "Y333T", "Y322M", "D329T", "K99E", "M231F", "WT", neg_control_K214)
different_colors_set <- c("H358I"="#332288ff", "K360I"="#117733ff", "M328I"="#44aa99ff", "Y333T"="#88cceeff", "Y322M"="#ddcc77ff", "D329T"="#cc6677ff", "K99E"="#aa4499ff", "M231F"="#882255ff", "WT"="black", neg_control_K214="darkgray")
possibly_specific_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target %in% possibly_specific_variants))
possibly_specific_subset$sgRNA_target <- factor(possibly_specific_subset$sgRNA_target, levels=c(unique(subset(possibly_specific_subset, !possibly_specific_subset$sgRNA_target %in% c(neg_control_K214, "WT"))$sgRNA_target), neg_control_K214, "WT"))
p <- lineplot_CVinterval_severalColours_meanlog2(possibly_specific_subset) + labs(title="most different variants N2, O2 feeds") + scale_color_manual(values=different_colors_set) + scale_fill_manual(values=different_colors_set) #+ scale_linetype_manual(values=c("WT"="solid", possibly_specific_variants[9]=44, possibly_specific_variants[1]=88, possibly_specific_variants[2]=13, possibly_specific_variants[3]=1343, possibly_specific_variants[4]=131343, possibly_specific_variants[5]=73, possibly_specific_variants[6]=2262, possibly_specific_variants[7]=112233, possibly_specific_variants[8]=11223344))
p
ggsave("../lfcSE_weighted_outputImages/pdf/mostDifferent_CLN2_CLO2_timeLinePlot.pdf", plot=p, width=10, height=10)
ggsave("../lfcSE_weighted_outputImages/png/mostDifferent_CLN2_CLO2_variants_timeLinePlot.png", plot=p, width=10, height=10)

a <- pivot_wider(unique(subset(fitness_data, fitness_data$sgRNA_target %in% possibly_specific_variants)[,c("sgRNA_target", "condition", "norm", "p_fit_adj_WT", "num_barcodes")]), values_from=c("norm", "p_fit_adj_WT"), names_from=condition)
a$norm_multiply <- a$norm_CL_N2 * a$norm_CL_O2 * a$norm_LD
a[order(a$norm_multiply, decreasing = TRUE),]

4.2 Compare light-dark and continuous light at the same gas feed

lm_CLO2_LD <- lm(norm_LD ~ norm_CL_O2, wide_fitness)
summary(lm_CLO2_LD)

Call:
lm(formula = norm_LD ~ norm_CL_O2, data = wide_fitness)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.24578 -0.09276  0.00288  0.09357  1.31207 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.143199   0.001874   76.41   <2e-16 ***
norm_CL_O2  0.607644   0.003211  189.25   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.159 on 13964 degrees of freedom
Multiple R-squared:  0.7195,    Adjusted R-squared:  0.7195 
F-statistic: 3.582e+04 on 1 and 13964 DF,  p-value: < 2.2e-16
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_LD, method = 'spearman')
correlation

    Spearman's rank correlation rho

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_LD
S = 7.9154e+10, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8256552 
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_LD, method = 'pearson')
correlation

    Pearson's product-moment correlation

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_LD
t = 189.25, df = 13964, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8435058 0.8528124
sample estimates:
      cor 
0.8482246 
CL_LD_cutoff <- 0.32
barcode_cutoff <- 2
adjp_cutoff <- 0.05

wide_fitness_LD_OL <- wide_fitness

wide_fitness_LD_OL$distance_lm <- wide_fitness_LD_OL$norm_LD - (lm_CLO2_LD$coefficients[1] + lm_CLO2_LD$coefficients[2] * wide_fitness_LD_OL$norm_CL_O2)
wide_fitness_LD_OL[order(wide_fitness_LD_OL$distance_lm, decreasing = TRUE),][1:10,]

# coloring according to differential expression
wide_fitness_LD_OL$diff <- "NO"
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm > 0] <- "LD"
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm < 0] <- "CL_O2"
#wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm > CL_LD_cutoff] <- "LD" # if using cut-off
#wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm < (-CL_LD_cutoff)] <- "CL_O2" # if using cut-off
wide_fitness_LD_OL[wide_fitness_LD_OL$sgRNA_target=="WT",]$diff <- "WT"

p <- ggplot(wide_fitness_LD_OL, aes(x=norm_CL_O2, y=norm_LD, color=diff, shape=WT_not_WT)) + geom_point(aes(size=WT_not_WT, alpha=WT_not_WT), show.legend=FALSE) + scale_shape_manual(values=WT_shape) + scale_size_manual(values=WT_size) + scale_alpha_manual(values=WT_alpha) + theme_light() + labs(y="Weighted mean fitness value in light-dark cycles, 5% CO2, 75% N2, 20% O2", x="Weighted mean fitness value in continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_LD$coefficients[1],slope=lm_CLO2_LD$coefficients[2],linetype="dashed",color="black")+ scale_colour_manual(values = dotplot_colors) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) #+ xlim(-6, +7) +ylim(-6,+7)  + geom_abline(intercept=lm_CLO2_LD$coefficients[1]-CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") + geom_abline(intercept=lm_CLO2_LD$coefficients[1]+CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") 
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLO2_LD_withoutSignif_withoutLabeling.pdf", plot=p, width=4, height=4, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLO2_LD_withoutSignif_withoutLabeling.png", plot=p, width=4, height=4, units="cm")
p

Run Wilcoxon test to check for significant differences between conditions.

subset_signif_highLD <- subset(subset_signif_highLD, subset_signif_highLD$condition %in% c("LD", "CL_O2") & subset_signif_highLD$time==0.0)

get_controls <- function(cond_spec, sgRNA_spec){
  control_table <- subset_signif_highLD[subset_signif_highLD$condition != unique(cond_spec) & subset_signif_highLD$sgRNA_target == unique(sgRNA_spec) & subset_signif_highLD$time == 0,]
  control_table$fitness
}

subset_signif_highLD <- dplyr::left_join(
  subset_signif_highLD,
  subset_signif_highLD %>%
    dplyr::group_by(sgRNA_target, condition, time) %>%
    dplyr::summarize(
      .groups = "keep",
      # apply Wilcoxon rank sum test against other condition
      p_fitness_condition = stats::wilcox.test(
        x = fitness,
        y = get_controls(condition, sgRNA_target),
        paired = TRUE,
        alternative = "two.sided"
        )$p.value
      ),
  by = c("sgRNA_target", "condition", "time")
  ) 

subset_signif_highLD <- subset_signif_highLD %>%
  group_by(condition, time) %>%
  mutate(
    p_fitness_condition_adj = stats::p.adjust(p_fitness_condition, method = "BH")
    )
subset_signif_highLD_red <- unique(subset_signif_highLD[,c("sgRNA_target", "norm", "condition", "num_barcodes", "p_fitness_condition_adj")])
subset_signif_highLD_red <- pivot_wider(subset_signif_highLD_red, values_from =c(norm,p_fitness_condition_adj), names_from=condition)

# labels, alpha and size according to significance
wide_fitness_LD_OL$signif <- "NO"
wide_fitness_LD_OL$signif[wide_fitness_LD_OL$sgRNA_target %in% unique(subset(subset_signif_highLD, subset_signif_highLD$p_fitness_condition_adj < 0.4))$sgRNA_target] <- "SIG"
wide_fitness_LD_OL$delabel <- NA
wide_fitness_LD_OL$delabel[wide_fitness_LD_OL$signif =="SIG"] <- as.character(wide_fitness_LD_OL$sgRNA_target[wide_fitness_LD_OL$signif =="SIG"])
wide_fitness_CL_OL[wide_fitness_CL_OL$sgRNA_target=="WT",]$signif <- "WT" # to ensure WT is visible

p <- ggplot(wide_fitness_LD_OL, aes(x=norm_CL_O2, y=norm_LD, color=diff, label=delabel, shape=WT_not_WT)) + scale_shape_manual(values=WT_shape) + geom_point(aes(size=signif, alpha=signif), show.legend=FALSE) + scale_size_manual(values=signif_size) + scale_alpha_manual(values=signif_alpha) + theme_light() + labs(y="Weighted mean fitness value at continuous light, 5% CO2, 95% N2, 0% O2", x="Weighted mean fitness value at continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_LD$coefficients[1],slope=lm_CLO2_LD$coefficients[2],linetype="dashed",color="black") + scale_colour_manual(values = dotplot_colors) + geom_text_repel() #+ xlim(0.75, 2.5) +ylim(0.75,2.25)
p
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_LD_CLO2_Wilcox_signif.pdf", plot=p, width=12.5, height=12.5, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_LD_CLO2_Wilcox_signif.png", plot=p, width=12.5, height=12.5, units="cm")


wide_fitness_CL_OL[wide_fitness_LD_OL$signif=="SIG",][order(wide_fitness_LD_OL[wide_fitness_LD_OL$signif=="SIG",]$norm_LD, decreasing=TRUE),]
## coloring according to differential expression
wide_fitness_LD_OL$diff <- "NO"
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm > CL_LD_cutoff] <- "LD" # if using cut-off
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm < (-CL_LD_cutoff)] <- "CL_O2" # if using cut-off
wide_fitness_LD_OL[wide_fitness_LD_OL$sgRNA_target=="WT",]$diff <- "WT"

# labels, alpha and size according to significant difference to WT variant
wide_fitness_LD_OL$signif <- "NO"
wide_fitness_LD_OL$signif[wide_fitness_LD_OL$p_fit_adj_WT_CL_O2 < adjp_cutoff & wide_fitness_LD_OL$p_fit_adj_WT_LD < adjp_cutoff & (wide_fitness_LD_OL$diff=="LD" | wide_fitness_LD_OL$diff=="CL_O2") & wide_fitness_LD_OL$num_barcodes >= barcode_cutoff] <- "SIG"
signif_size <- c("NO"=0.2, "SIG"=0.3)
signif_alpha <- c("NO"=0.2, "SIG"=0.8)
wide_fitness_LD_OL$delabel <- as.character(wide_fitness_LD_OL$sgRNA_target)
wide_fitness_LD_OL$delabel[wide_fitness_LD_OL$signif !="SIG"] <- NA

p <- ggplot(wide_fitness_LD_OL, aes(x=norm_CL_O2, y=norm_LD, color=diff, shape=WT_not_WT, label=delabel)) + geom_point(aes(size=signif, alpha=signif), show.legend=FALSE) + scale_shape_manual(values=WT_shape) + scale_size_manual(values=signif_size) + scale_alpha_manual(values=signif_alpha) + theme_light() + labs(y="Weighted mean fitness value in light-dark cycles, 5% CO2, 75% N2, 20% O2", x="Weighted mean fitness value in continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_LD$coefficients[1],slope=lm_CLO2_LD$coefficients[2],linetype="dashed",color="black")+ scale_colour_manual(values = dotplot_colors)  + geom_abline(intercept=lm_CLO2_LD$coefficients[1]-CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") + geom_abline(intercept=lm_CLO2_LD$coefficients[1]+CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") + geom_text_repel() #  + xlim(-6, +7) +ylim(-6,+7)
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLO2_LD_attempt_findDifferentVariants.pdf", plot=p, width=12.5, height=12.5, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLO2_LD_attempt_findDifferentVariants.png", plot=p, width=12.5, height=12.5, units="cm")
p


wide_fitness_LD_OL_sig <- subset(wide_fitness_LD_OL, wide_fitness_LD_OL$signif=="SIG")
wide_fitness_LD_OL_sig[order(wide_fitness_LD_OL_sig$distance_lm, decreasing = TRUE),][1:10,]
possibly_higher_inLD <- c("K456H", "G432S", "K99N", "H126G","WT", neg_control_K214)
different_colors_set <- c("K456H"="#332288ff", "G432S"="#117733ff", "H358M"="#44aa99ff", "H126N"="#88cceeff", "H126G"="#ddcc77ff", "K99N"="#cc6677ff", "WT"="black", neg_control_K214="darkgray")
single_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% possibly_higher_inLD)
p <- lineplot_CVinterval_severalColours_meanlog2(single_subset) + labs(title="most different variants") + scale_color_manual(values=different_colors_set) + scale_fill_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/mostDifferent_LD_CLO2_timeLinePlot.pdf", plot=p, width=10, height=10)
ggsave("../lfcSE_weighted_outputImages/png/mostDifferent_LD_CLO2_variants_timeLinePlot.png", plot=p, width=10, height=10)


unique(subset(fitness_data, fitness_data$sgRNA_target %in% possibly_higher_inLD)[,c("sgRNA_target", "condition", "norm", "p_fit_adj_WT", "num_barcodes")])

5 Generally interesting mutants

In the following, an attempt is made to pinpoint some interesting variants that are worth testing. The following chunk of code selects all variants which are performing well under all conditions and are significantly different to the base variant. Furthermore, only variants with at least two barcodes are taken into account.

columns <- c("sgRNA_target",  "condition", "norm", "p_fit_adj_WT", "num_barcodes", "number_muts")
fitness_data_goodVariants <- pivot_wider(unique(fitness_data[,columns]), values_from = c("norm", "p_fit_adj_WT"), names_from = condition)
fitness_data_goodVariants <- unique(subset(fitness_data_goodVariants, fitness_data_goodVariants$p_fit_adj_WT_CL_N2 < 0.05 & fitness_data_goodVariants$p_fit_adj_WT_CL_O2 < 0.05 & fitness_data_goodVariants$p_fit_adj_WT_LD < 0.05 & fitness_data_goodVariants$norm_CL_N2 > 1 & fitness_data_goodVariants$norm_CL_O2 > 1 & fitness_data_goodVariants$norm_LD > 1 & fitness_data_goodVariants$num_barcodes > 1))

length(unique(fitness_data_goodVariants$sgRNA_target))
[1] 23
length(unique(fitness_data_goodVariants$sgRNA_target))/length(unique(fitness_data$sgRNA_target))
[1] 0.001646857
fitness_data_goodVariants$combined_norms <- fitness_data_goodVariants$norm_CL_N2 * fitness_data_goodVariants$norm_CL_O2 * fitness_data_goodVariants$norm_LD
print(fitness_data_goodVariants[order(fitness_data_goodVariants$combined_norms, decreasing = TRUE),])
write_csv(fitness_data_goodVariants[order(fitness_data_goodVariants$combined_norms, decreasing = TRUE),], "../lfcSE_weighted_outputImages/goodVariants.csv")
highScoring_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H126N", "H126M", "W128I", "K360I", "K360M", "Q142D", "K233C", "H358C", "K360L", "H358L", "K99N", "K360C", "Y333V", "H126G", "M154E,K261A,Q406D,S446T", "Q142E", "M345Q", "F104V", "V98Q", "K261A,H265A,Q406D,S446I", "K99T", "K233L", "M345V", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(highScoring_subset) + labs(title="Fittest variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/highest_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/highest_variants_timeLinePlot.png", plot=p)

Partially, exchanges at the same amino acid position score quite similarly. We cannot distinguish their fitness on basis of our data.

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H126N", "H126M", "H126G", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit H126 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/H126_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/H126_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K360I", "K360M", "K360L", "K360C", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit K360 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K360_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K360_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H358C", "H358L", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit H358 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/H358_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/H358_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("Q142D", "Q142E", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit Q142 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/Q142_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/Q142_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("M345Q", "M345V", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit M345 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/M345_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/M345_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K233C", "K233L", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit K233 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K233_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K233_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K99N", "K99T", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit K99 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K99_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K99_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("W128I", "Y333V", "F104V", "V98Q", "WT", neg_control_K214))
different_colors_set <- c("W128I"="#332288ff", "Y333V"="#117733ff", "V98Q"="#88cceeff", "F104V"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray") #different_colors_set <- c("W128I"="#332288ff", "Y333V"="#117733ff", "F104V"="#44aa99ff", "V98Q"="#88cceeff", "F104V"="#ddcc77ff", "K99N"="#cc6677ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit variants with 95% CI") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/remaining_highScoring_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/remaining_highScoring_variants_timeLinePlot.png", plot=p)

6 Plot epistatic effects

6.1 General overview over epistasis in data set

From Miton et al., 2016 (https://onlinelibrary.wiley.com/doi/full/10.1002/pro.2876): “We calculated the fold change in enzyme fitness (F) provided by a mutation i on the wild-type background (ΔFwt,i=Fwt+i/Fwt) and the change caused by the same mutation on the intermediate variant j in the trajectory, (ΔFj,i=Fj+i/Fj) […] epistasis was determined by comparing the fold changes in the trajectory over the wild-type background (ΔFj,i/ΔFwt,i). For the sake of simplicity, we considered that ≥1.5-fold change is significant, and less than 1.5-fold change is neutral (or nearly neutral). […] Neutral designates mutations that show less than 1.5-fold change in enzyme fitness on both backgrounds (0.7 < [ΔFj,i and ΔFwt,i] < 1.5). (ii - v) Functional refers to non-neutral mutations that exhibit >1.5-fold improvement in either genetic background (ΔFj,i or ΔFwt,i > 1.5). Among functional mutations, ii) no epistasis refers to mutations that do not significantly alter the enzyme fitness depending on the background (mutations are additive, ΔFj,i/ΔFwt,i ∼ 1). (iii – iv) Positive epistasis applies to a mutation that becomes more beneficial when combined with prior substitutions on the trajectory, compared to its effect on the wild-type background (ΔFj,i/ΔFwt,i > 1.5). It can be divided in two subclasses: iii) Positive magnitude epistasis refers to cases where the effect is neutral or positive on the wild-type background and is further amplified on the trajectory (ΔFj,i ≫ ΔFwt, i > 0.7 or ΔFj, i ≫ ΔFwt, i > 1.5); and iv) Positive sign epistasis, which refers to mutations causing a deleterious effect on the wild-type background but a beneficial effect on the trajectory (ΔFwt, i <0.7 and ΔFj, i > 1.5). v) Negative epistasis applies to a mutation that becomes less beneficial on the trajectory background compared to its original effect on the wild type (ΔFj, i/ΔFwt, i <0.7).”

Identify cut-off values for good / bad variants - use adj. p values < 0.05 to do so, question “can I significantly distinguish this variant from the base variant?” Seems as if differences as small as a normed value of 1.1 are often already significant compared to the base variant - I assume a normed value of 1.25 is about right.

combinatorial_wide <- subset(combinatorial_wide, combinatorial_wide$p_fit_adj_WT<0.05) # in case things were changed above
a <- unique(subset(combinatorial_wide, combinatorial_wide$norm > 1.0)$norm)
a <- a[order(a)]
a[1:20]
 [1] 1.066177 1.077010 1.084487 1.089128 1.089533 1.099646 1.102634 1.103528 1.106683
[10] 1.108989 1.109216 1.113865 1.116721 1.118302 1.120763 1.121159 1.122081 1.123697
[19] 1.124440 1.127339

Also, differences as small as 0.9 compared to 1.0 can be distinguished - so I guess a good cut-off might be 0.8.

a <- unique(subset(combinatorial_wide, combinatorial_wide$norm < 1.0)$norm)
a <- a[order(a, decreasing=TRUE)]
a[1:20]
 [1] 0.9386771 0.9381779 0.9351133 0.9347565 0.9211815 0.9185717 0.9162180 0.9156449
 [9] 0.9141355 0.9107536 0.9102129 0.9094565 0.9087976 0.9082542 0.9081407 0.9080229
[17] 0.9070452 0.9065860 0.9040132 0.9030803
summarize_dataframe <- unique(dplyr::summarize(.data=epistatic_table,
      .by = c(Exchange, Condition),
    # value in WT background
    value_background = unique(.data[["Exchange_value_and_ratio_in_WT"]]),
    # number neg. effect in higher order
    number_negative_exchanges = sum(.data[["Variant_ratio"]] < 0.8), # adjusted to 0.8 according to observations detailed above from originally 0.7 in paper
    # number neutral effect in higher order 
    number_neutral_exchanges = sum(.data[["Variant_ratio"]] > 0.8 & .data[["Variant_ratio"]] < 1.25), # adjusted to 0.8 and 1.2 from 0.7 and 1.5
    # number pos. effect in higher order
    number_positive_exchanges = sum(.data[["Variant_ratio"]] > 1.25), # adjusted from 1.5 in paper to 1.2 according to observations detailed above
    # no epistasis (0.7 < value < 1.5)
    number_no_epistasis = sum(.data[["ratio_WT_higherOrder"]] > 0.7 & .data[["ratio_WT_higherOrder"]] < 1.5),
    # positive epistasis (>1.5)
    number_positive_epistasis = sum(.data[["ratio_WT_higherOrder"]] > 1.5),
    # negative epistasis (<0.7)
    number_negative_epistasis = sum(.data[["ratio_WT_higherOrder"]] < 0.7),
    # total higher exchange
    number_higher_exchanges = n()
    ))
exchanges_dataset <- summarize_dataframe[,c("Exchange", "Condition", "number_negative_exchanges", "number_neutral_exchanges", "number_positive_exchanges")]
exchanges_dataset <- pivot_longer(exchanges_dataset, !c(Exchange, Condition), values_to="number", names_to="exchanges")

exchanges_dataset$Exchange <- factor(exchanges_dataset$Exchange, levels=rev(c("H141L", "H141V", "M154D", "M154E", "M154K", "M154S", "K261A", "K261D", "K261E", "K261F", "H265A", "H265E", "H265K", "H265R", "V337A", "V337S", "Q406D", "Q406E", "S446A", "S446I", "S446T")))

colors_exchanges <- brewer.pal(n = 9, name = "Blues")[c(3,6,9)]
names(colors_exchanges) <- c("number_positive_exchanges", "number_neutral_exchanges", "number_negative_exchanges")

p <- ggplot(exchanges_dataset) +
  geom_bar(aes(x = number, y = Exchange, fill = exchanges),
           position = "fill",
           stat = "identity")  + 
  scale_fill_manual(values=colors_exchanges) +
  facet_grid(~ Condition, switch = "x") + 
  theme_light() + 
  theme(strip.placement = "outside",
        strip.background = element_rect(fill = NA, color = "white"),
        panel.grid.minor = element_blank(), 
        axis.text=element_text(size=8), 
        axis.title=element_text(size=8), 
        legend.text=element_text(size=8),
        strip.text.x = element_text(size = 8, colour = "black"))
p 
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeExchanges_higherOrdervariants.pdf", plot=p, width=7.5, height=3)
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeExchanges_higherOrdervariants.png", plot=p, width=7.5, height=3)

epistasis_dataset <- summarize_dataframe[,c("Exchange", "Condition", "number_no_epistasis", "number_positive_epistasis", "number_negative_epistasis")]
epistasis_dataset <- pivot_longer(epistasis_dataset, !c(Exchange, Condition), values_to="number", names_to="epistasis")

epistasis_dataset$Exchange <- factor(epistasis_dataset$Exchange, levels=rev(c("H141L", "H141V", "M154D", "M154E", "M154K", "M154S", "K261A", "K261D", "K261E", "K261F", "H265A", "H265E", "H265K", "H265R", "V337A", "V337S", "Q406D", "Q406E", "S446A", "S446I", "S446T")))

colors_epistasis <- brewer.pal(n = 9, name = "Blues")[c(3,6,9)]
names(colors_epistasis) <- c("number_positive_epistasis", "number_no_epistasis", "number_negative_epistasis")

p <- ggplot(epistasis_dataset) +
  geom_bar(aes(x = number, y = Exchange, fill = epistasis),
           position = "fill",
           stat = "identity") +
  scale_fill_manual(values=colors_epistasis) +
  facet_grid(~ Condition, switch = "x") + 
  theme_light() + 
  theme(strip.placement = "outside",
        strip.background = element_rect(fill = NA, color = "white"),
        panel.grid.minor = element_blank(), 
        axis.text=element_text(size=8), 
        axis.title=element_text(size=8), 
        legend.text=element_text(size=8),
        strip.text.x = element_text(size = 8, colour = "black"))
p
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeEpistasis_higherOrdervariants.pdf", plot=p, width=7.5, height=3)
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeEpistasis_higherOrdervariants.png", plot=p, width=7.5, height=3)

6.2 Examples of epistasis

epistatic_table_wide <- pivot_wider(epistatic_table[,c("Condition", "Exchange_value_and_ratio_in_WT", "Exchange", "Variant", "Variant_value", "ratio_WT_higherOrder")], names_from=c("Condition"), values_from=c("Variant_value", "ratio_WT_higherOrder", "Exchange_value_and_ratio_in_WT"))
epistatic_table_wide <- subset(epistatic_table_wide, epistatic_table_wide$ratio_WT_higherOrder_CL_N2>1.5 & epistatic_table_wide$ratio_WT_higherOrder_CL_O2 > 1.5 & epistatic_table_wide$ratio_WT_higherOrder_LD > 1.5 & epistatic_table_wide$Variant_value_CL_N2 > 1.0 & epistatic_table_wide$Variant_value_CL_O2 > 1.0 & epistatic_table_wide$Variant_value_LD > 1.0)
nrow(epistatic_table_wide)
[1] 14
epistatic_table_wide$ep_product <- epistatic_table_wide$ratio_WT_higherOrder_CL_N2 * epistatic_table_wide$ratio_WT_higherOrder_CL_O2 * epistatic_table_wide$ratio_WT_higherOrder_LD
epistatic_table_wide[order(epistatic_table_wide$ep_product, decreasing=TRUE),]

Check again if any of the “good, recovered” strains are just an artefact !

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("M154K", "M154K,H265E,Q406E,S446T", "H265E,Q406E,S446T", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("M154K"="#88cceeff", "M154K,H265E,Q406E,S446T"="#117733ff", "H265E,Q406E,S446T"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving M154K") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261D", "K261D,H265E,Q406E,S446T", "H265E,Q406E,S446T", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261D"="#88cceeff", "K261D,H265E,Q406E,S446T"="#117733ff", "H265E,Q406E,S446T"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving K261D") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("V337A", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I", "H141V,M154A,K261F,H265A,Q406E,S446I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("V337A"="#88cceeff", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I"="#117733ff", "H141V,M154A,K261F,H265A,Q406E,S446I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving V337A") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/epistatic-effects-V337A.pdf", plot=p, width=20, height=20)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("Q406E", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I", "H141V,M154A,K261F,H265A,V337A,S446I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("Q406E"="#88cceeff", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I"="#117733ff", "H141V,M154A,K261F,H265A,V337A,S446I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving Q406E") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/epistatic-effects-Q406E.pdf", plot=p, width=20, height=20)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("Q406E", "H141V,M154A,Q406E", "H141V,M154A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("Q406E"="#88cceeff", "H141V,M154A,Q406E"="#117733ff", "H141V,M154A"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving Q406E") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261A", "K261A,H265K,Q406E,S446A", "H265K,Q406E,S446A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261A"="#88cceeff", "K261A,H265K,Q406E,S446A"="#117733ff", "H265K,Q406E,S446A"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H265A", "M154A,K261A,H265A,Q406D", "M154A,K261A,Q406D", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("H265A"="#88cceeff", "M154A,K261A,H265A,Q406D"="#117733ff", "M154A,K261A,Q406D"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving H265A") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("S446T", "M154A,K261A,Q406D,S446T", "M154A,K261A,Q406D", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("S446T"="#88cceeff", "M154A,K261A,Q406D,S446T"="#117733ff", "M154A,K261A,Q406D"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving S446T") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261F", "K261F,H265K,Q406D,S446I", "H265K,Q406D,S446I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261F"="#88cceeff", "K261F,H265K,Q406D,S446I"="#117733ff", "H265K,Q406D,S446I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving K261F") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H265E", "K261D,H265E,Q406E,S446T", "K261D,Q406E,S446T", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("H265E"="#88cceeff", "K261D,H265E,Q406E,S446T"="#117733ff", "K261D,Q406E,S446T"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving H265E") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261A", "K261A,H265K,Q406E,S446A", "H265K,Q406E,S446A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261A"="#88cceeff", "K261A,H265K,Q406E,S446A"="#117733ff", "H265K,Q406E,S446A"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving K261A") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p

7 Variants for which mutant strains exist

“variant H141V,K261A,H265K,Q406E,S446I with the highest fitness value in the library, but shitty adjusted p value, is H141V,K261A,H265K,Q406E,S446I (norm=2.18, p.adj=0.107), alternative option for showing epistasis (besides D and E), needs control strains H141V, K261A, Q406E, S446I and H265K to show that H265K is detrimental when introduced on its own, beneficial in background of quardruple mutant”

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,K261A,H265K,Q406E,S446I", "H141V,K261A,Q406E,S446I", "H265K", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects and 'strain A'")
p

H141V, M154A, K261A, H265A, Q406E, S446T other alternative option for showing epistasis (besides E), there should also be a corresponding mutant with 5 exchanges (H141V, M154A, K261A, Q406E, S446T) and then the comparison that H265A is detrimental on its own, but beneficial in the background of the mutant with 5 exchagnes

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,M154A,K261A,H265A,Q406E,S446T", "H141V,M154A,K261A,Q406E,S446T", "H265A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects and 'strain D'")
p

H141V, K261E, H265A, S446A “fittest combinatorial mutant variant with a significant difference has four amino acid exchanges: H141V,K261E,H265A,S446A (norm=1.56, p.adj=0.0259) , main line Figure showing epistasis, there should also be a corresponding triple mutant (H141V, K261E, S446A) showing that H265A on its own is detrimental, in background of H141V, K261E, S446A beneficial”

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,K261E,H265A,S446A", "H141V,K261E,S446A", "H265A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects and 'strain E'")
p

Variant underlying the suggested higher-order variants: H141V, K261A, H265A, Q406E, S446T

different_colors_set <- c("W128I"="#88cceeff", "H141V,K261A,H265A,Q406E,S446T"="#117733ff", "K360I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,K261A,H265A,Q406E,S446T", "W128I", "K360I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Variant underlying B, C, Bfix and single exchanges introduced in these variants") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/variant_underlying_B-C-Bfix.pdf", plot=p, width=20, height=20)

W128I single exchange with high fitness value, H358S single exchange with high fitness value, K360I single exchange with high fitness value

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("W128I", "H358S", "K360I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Single exchanges added cloned as single mutant strains")
p

Higher order variant with V98D, F104C, W128K, Q142D, K233I, V270L, M345I, H358S

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("V98D", "F104C", "W128K", "Q142D", "K233I", "V270L", "M345I", "H358S", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Single exchanges underlying higher order variant ('M8')")
p

8 Other analyses for combinatorial library

single_muts_combinatorial <- pivot_wider(unique(subset(fitness_data, fitness_data$category=="combiANDsatur")[,columns]), names_from=condition, values_from=c(norm, p_fit_adj_WT))
single_muts_combinatorial$norm_multiply <- single_muts_combinatorial$norm_CL_N2 * single_muts_combinatorial$norm_CL_O2 * single_muts_combinatorial$norm_LD
write.csv(single_muts_combinatorial, file="../lfcSE_weighted_outputImages/single_site_exchanges_combinatorialLibrary.csv")
single_muts_combinatorial[order(single_muts_combinatorial$norm_multiply, decreasing=TRUE),c(1,4,5,6,10,2,3,7,8,9)]
combi_single_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("WT", "H265K", "H265R", "V337A", "V337S", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(combi_single_subset) + labs(title="Most detrimental single variants in combinatorial library")
p

for(n_mut in 2:7){
  print(n_mut)
  muts_combinatorial <- pivot_wider(unique(subset(fitness_data, fitness_data$number_muts==n_mut)[,columns]), names_from=condition, values_from=c(norm, p_fit_adj_WT))
  muts_combinatorial$norm_multiply <- muts_combinatorial$norm_CL_N2 * muts_combinatorial$norm_CL_O2 * muts_combinatorial$norm_LD
  print("Sorted according to product of different normed fitness values")
  print(head(muts_combinatorial[order(muts_combinatorial$norm_multiply, decreasing=TRUE),c(1,4,5,6,10,2,3,7,8,9)]))
  muts_combinatorial <- subset(muts_combinatorial, muts_combinatorial$norm_CL_N2 > 1.0 & muts_combinatorial$norm_CL_O2 > 1.0 & muts_combinatorial$norm_LD > 1.0)
  muts_combinatorial <- subset(muts_combinatorial, (muts_combinatorial$p_fit_adj_WT_CL_N2 < 0.05 | muts_combinatorial$p_fit_adj_WT_CL_O2 < 0.05 | muts_combinatorial$p_fit_adj_WT_LD < 0.05) & muts_combinatorial$norm_CL_N2 > 1.0 & muts_combinatorial$norm_CL_O2 > 1.0 & muts_combinatorial$norm_LD > 1.0) 
  print("Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions")
  print(head(muts_combinatorial[order(muts_combinatorial$norm_multiply, decreasing=TRUE),c(1,4,5,6,10,2,3,7,8,9)]))
}
[1] 2
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 3
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 4
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 5
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 6
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 7
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"

9 Truncated variants

truncated_variants <- subset(fitness_data, fitness_data$category =="notExpected")
length(unique(truncated_variants$sgRNA_target))
[1] 34
truncated_wide <- pivot_wider(unique(truncated_variants[,c("sgRNA_target", "norm", "condition", "p_fit_adj_WT", "num_barcodes")]), values_from =c(norm,p_fit_adj_WT), names_from=condition)
truncated_wide$norm_product <- truncated_wide$norm_CL_N2 * truncated_wide$norm_CL_O2 * truncated_wide$norm_LD
truncated_wide[order(truncated_wide$norm_product, decreasing=TRUE),]
truncated_subset <- combi_single_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("G395V,STOP395", "F405S,Q406R,N407T,STOP407", "WT", neg_control_K214))
truncated_subset$sgRNA_target <- factor(truncated_subset$sgRNA_target, levels=c("G395V,STOP395", "F405S,Q406R,N407T,STOP407", "WT", neg_control_K214))
different_colors_set <- c("G395V,STOP395"="#44aa99ff", "F405S,Q406R,N407T,STOP407"="#aa4499ff", "WT"="black", "K214R"="darkgray")
different_linetypes_set <- c("G395V,STOP395"=44, "F405S,Q406R,N407T,STOP407"=1343, "WT"="solid", "K214R"="longdash")
p <- lineplot_CVinterval_severalColours_meanlog2(truncated_subset) + scale_linetype_manual(values=different_linetypes_set) + scale_color_manual(values=different_colors_set) + scale_fill_manual(values=different_colors_set)
ggsave("../lfcSE_weighted_outputImages/pdf/truncated_variants_performing_well.pdf", plot=p, height=1.75, width=6, units="in")
p

11 Create files for ProteinNPT

Compare https://github.com/OATML-Markslab/ProteinNPT. Expects a .csv file. “At a minimum this file requires 2 fields: mutated_sequence (full sequence of amino acids) and DMS_score (assay measurement). If no fold variable is included in the assay file, the pipeline script will automatically create a fold_random_5 variable, assigning each mutant to folds 0-4 at random. You may also use your own cross-validation scheme (eg., assign all training sequences to fold 0, all test sequences to fold 1). To that end, you only need to pass to the pipeline script the name of that fold variable via the fold_variable_name argument and specify the index of the test fold via the test_fold_index argument (if test_fold_index is not passed as argument, the script will automatically perform a full cross-validation, rotating the test fold index at each iteration).”

Create a file for a) predicting higher-order combinations - separate amino acid positions into 5 folds –> add folds using python - three files for each condition

Use python script single_muts_train_for_combi.py to assign folds for training etc., output saturational_proteinNPT_with_foldChange.csv

Regarding folds: “We develop 3 distinct cross-validation schemes to assess the ability of each model to extrapolate to positions not encountered during training. In the Random scheme, commonly-used in other supervised fitness prediction benchmarks [Rao et al., 2019, Dallago et al., 2022], each mutation is randomly allocated to one of five distinct folds. In the Contiguous scheme, the sequence is split into five contiguous segments along its length, with mutations assigned to each segment based on the position they occur in the sequence. Lastly, the Modulo scheme uses the modulo operator to assign mutated positions to each fold. For example, position 1 is assigned to fold 1, position 2 to fold 2, and so on, looping back to fold 1 at position 6. This pattern continues throughout the sequence. We note that there is no inherent issue with using a Random cross-validation scheme to estimate the performance of predictive models. However, the conclusions drawn and the generalizability claims based on it require careful consideration.” –> use modulo scheme for assigning folds to saturational library

  1. predicting more combinations
saturational_for_proteinNPT <- unique(subset(fitness_data, fitness_data$number_muts==1)[,c("sgRNA_target", "norm", "baseAA", "condition")])
saturational_for_proteinNPT <- pivot_wider(saturational_for_proteinNPT, names_from=condition, values_from=norm)
write_csv(saturational_for_proteinNPT, "../lfcSE_weighted_outputImages/csv_for_proteinNPT/saturational_for_proteinNPT.csv")

all_for_proteinNPT <- unique(subset(fitness_data, fitness_data$number_muts>1)[,c("sgRNA_target", "norm", "condition")])
all_for_proteinNPT <- pivot_wider(all_for_proteinNPT, names_from=condition, values_from=norm)
write_tsv(all_for_proteinNPT, "../lfcSE_weighted_outputImages/csv_for_proteinNPT/combinatorial_for_proteinNPT.tsv")

12 Create file for Supplement

columns_for_table <- c("sgRNA_target", "num_barcodes", "number_muts", "norm", "condition", "p_fit_adj_WT", "EVcoup_predict", "DeepSeq_predict", "MSA_Transform", "proteinNPT_predict", "additive_score", "conservationScore", "absSAS", "relSAS", "dimer")

long_format <- unique(fitness_data[,columns_for_table])
write_csv(long_format, "../lfcSE_weighted_outputImages/SuppTable_all_fitness_values_long.csv")

wide_format <- pivot_wider(long_format, names_from=condition, values_from=c(norm, p_fit_adj_WT))
write_csv(wide_format, "../lfcSE_weighted_outputImages/SuppTable_all_fitness_values_wide.csv")

Session Info

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgcG9vbGVkIGdyb3d0aCBvZiBsYXJnZSBDYmJNIGxpYnJhcnkiCmF1dGhvcjogIlV0ZSBIb2ZmbWFubiIKZGF0ZTogJ0RBVEU6IGByIFN5cy50aW1lKClgJwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwpwYXJhbXM6CiAgY3B1czogMgogIGlucHV0X2RpcjogLi8KICBtZXRhOiAnJwotLS0KCmBgYHtyIGxvYWQtcmVxdWlyZWQtcGFja2FnZXMsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoRGVzY1Rvb2xzKQpsaWJyYXJ5KGNvbG9yYmxpbmRyKSAjaHR0cHM6Ly9naXRodWIuY29tL2NsYXVzd2lsa2UvY29sb3JibGluZHIKbGlicmFyeSh0aWR5cikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdnbmV3c2NhbGUpCmxpYnJhcnkoSGVhdHBsdXMpICMgYmlvY29uZHVjdG9yCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApICMgYmlvY29uZHVjdG9yCmxpYnJhcnkoZWRnZVIpCmxpYnJhcnkoTWZ1enopCmxpYnJhcnkoZ2dWZW5uRGlhZ3JhbSkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShwbHlyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgpgYGB7ciBjb2xvci1kZWZpbml0aW9ucywgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KY29sX2NvbWJpX3NhdCA9IGMoImNvbWJpbmF0b3JpYWwiPSIjNjliM2EyIiwgInNhdHVyYXRpb25hbCI9IiM0MDQwODAiLCAiV1QiPSJibGFjayIpCmNvbF9jb25kaXRpb25zID0gYygiQ0xfTjIiPSIjZTY5ZjAwZmYiLCAiQ0xfTzIiPSIjMDI3MmIyZmYiLCAiTEQiPSIjMDA1NjNlOTkiKQpgYGAKCiMgQnJpZWYgZGVzY3JpcHRpb24gb2YgZGF0YSBzZXQKCkEgbGlicmFyeSBvZiBSdWJpc2NvIHZhcmlhbnRzIGJhc2VkIG9uIGEgbXV0YW50IHZhcmlhbnQgb2YgKkdhbGxpb25lbGxhKiBzcC4gQ2JiTSB3YXMgZGVzaWduZWQuIFRoZSBDYmJNIHZhcmlhbnQgaXMgdHdvIGFtaW5vIGFjaWQgZXhjaGFuZ2VzIGF3YXkgZnJvbSB0aGUgd2lsZC10eXBlIHNlcXVlbmNlIGFuZCB3YXMgaWRlbnRpZmllZCBhcyBwYXJ0IG9mIGEgc21hbGwgbGlicmFyeSB3aGljaCB3YXMgdXNlZCB0byBlc3RhYmxpc2ggdGhlIHNjcmVlbmluZyBzeXN0ZW0uIEZvciByZWFzb25zIG9mIHNpbXBsaWNpdHksIGluIHRoZSBmb2xsb3dpbmcsICJXVCIgd2lsbCByZWZlciB0byB0aGlzIHZhcmlhbnQsIHdoaWNoIGlzIHRoZSAiYmFzZSIgb2YgdGhlIHdob2xlIGxpYnJhcnkuClRoZSBsYXJnZSBsaWJyYXJ5IGNvbnNpc3RzIG9mIDY3IHBvc2l0aW9ucywgd2hpY2ggd2VyZSBzZXBhcmF0ZWx5IGV4Y2hhbmdlZCBieSBhbnkgb3RoZXIgcG9zc2libGUgYW1pbm8gYWNpZCBhbmQgYSBjb21iaW5hdG9yaWFsIHBhcnQsIHdoaWNoIGlzIGJhc2VkIG9uIHNldmVuIGFtaW5vIGFjaWQgcG9zaXRpb25zLCB3aGljaCB3ZXJlIGV4Y2hhbmdlZCwgaW4gYSBjb21iaW5hdG9yaWFsIGZhc2hpb24sIGJ5IGEgZmV3IHNwZWNpZmljIHBvc3NpYmxlIHJlc2lkdWVzIHBlciBwb3NpdGlvbi4KCkJvdGggcGFydHMgb2YgdGhlIGxpYnJhcnkgd2VyZSBkZXNpZ25lZCB3aXRoIHRoZSBoZWxwIG9mIEVWbXV0YXRpb24gYW5kIERlZXBTZXF1ZW5jZSwgd2hpY2ggdXNlIHBoeWxvZ2VuZXRpYyBpbmZvcm1hdGlvbiB0byBwcmVkaWN0IGJlbmVmaWNpYWwgYW1pbm8gYWNpZCBleGNoYW5nZXMuIFRoZSBleGFjdCBtYW5uZXIgaG93IHByZWRpY3Rpb25zIHdlcmUgcGVyZm9ybWVkIGlzIGRlc2NyaWJlZCBhbG9uZyB3aXRoIHRoZSBzbWFsbCBsaWJyYXJ5IHRoZSAiYmFzZSIgbXV0YW50IHZhcmlhbnQgaXMgZGVyaXZlZCBmcm9tLgoKVGhlIGNvbXBsZXRlIGxpYnJhcnkgd2FzIHRyYW5zZm9ybWVkIGludG8gYSAqU3luZWNob2N5c3RpcyogaG9zdCBzdHJhaW4sIGluIHdoaWNoIHRyYW5zY3JpcHRpb24gb2YgdGhlIGVuZG9nZW5vdXMgUnViaXNjbyB0cmFuc2NyaXB0IGNhbiBiZSBpbmhpYml0ZWQgYnkgdGhlIGluZHVjdGlvbiBvZiBhIENSSVNQUiBpbmhpYml0aW9uIHN5c3RlbS4gRWFjaCB2YXJpYW50IGlzIHJlcHJlc2VudGVkIGJ5IG9uZSBvciBzZXZlcmFsIGNsb25lcywgd2hpY2ggY2FuIGJlIGRpc3Rpbmd1aXNoZWQgb24gYmFzZSBvZiB0aGVpciBOMjAgYmFyY29kZXMuIFRoZSBwb29sZWQgbGlicmFyeSBvZiB0aGVzZSBzdHJhaW5zIHdhcyBncm93biBpbiA4IHJlcGxpY2F0ZXMgYXQgZGlmZmVyZW50IGdhcyBmZWVkIGFuZCBsaWdodCBjb25kaXRpb25zIGluIHR1cmJpZG9zdGF0IG1vZGUgZm9yIGFwcHJveGltYXRlbHkgMTAgZ2VuZXJhdGlvbnMgKENMX04yOiBjb25zdGFudCBsaWdodCAzMDAgwrVFLCA1JSBDTzIsIDk1JSBOMiwgMCUgTzIsIENMX08yOiBjb25zdGFudCBsaWdodCAzMDAgwrVFLCA1JSBDTzIsIDc1JSBOMiwgMjAlIE8yLCBMRDogbGlnaHQtZGFyayBjeWNsZXMsIDUlIENPMiwgNzUlIE4yLCAyMCUgTzIpLiBCeSB0YWtpbmcgc2FtcGxlcyBmb3IgSWxsdW1pbmEgc2VxdWVuY2luZyByZWd1bGFybHksIHdlIHdlcmUgYWJsZSB0byB0cmFjayBhbG1vc3QgYWxsIHZhcmlhbnRzIHByZXNlbnQgYW5kIGFzc2lnbiBmaXRuZXNzIHZhbHVlcy4KCk5vdGUgdGhhdCBhbWlubyBhY2lkIG51bWJlcmluZyBpbiB0aGUgZm9sbG93aW5nIGFyZSBiYXNlZCBvbiB0aGUgc2VxdWVuY2UgaW5jbHVkaW5nIHRoZSBOLVN0cmVwIHRhZy4KCkZpdG5lc3MgdmFsdWVzIGFyZSBnaXZlbiBhcyBub3JtYWxpemVkIHZhbHVlcywgaS5lLiBhIHZhbHVlIG9mIDEuMCBlcXVhbHMgdGhlIGZpdG5lc3Mgb2YgdGhlIGJhc2UgdmFyaWFudCAoYWN0dWFsbHkgdGhlIHVuZGVybHlpbmcgQ2JiTSB2YXJpYW50IHdoaWNoIGlzIHR3byBhbWlubyBhY2lkIGV4Y2hhbmdlcyBhd2F5IGZyb20gd2lsZC10eXBlIENiYk0pIGFuZCAwLjAgZXF1YWxzIHRoZSBtZWRpYW4gdmFsdWUgb2YgSzIxNCB2YXJpYW50cyBwcmVzZW50IGluIHRoZSBkYXRhIHNldC4gSzIxNCBpcyB0aGUgY2FyYmFteWxhdGVkIGx5c2luZSB3aGljaCBpcyBlc3NlbnRpYWwgZm9yIGNhdGFseXNpcy4gRXhjaGFuZ2VzIG9mIHRoaXMgbHlzaW5lIHJlc2lkdWUgc2hvdWxkIHJlc3VsdCBpbiBjYXRhbHl0aWNhbGx5IGluYWN0aXZlIGVuenltZS4gTXV0YW50IHZhcmlhbnRzIHdpdGggd29yc2UgZml0bmVzcyB2YWx1ZXMgdGhhbiBLMjE0IHN1YnN0aXR1dGlvbnMgY2FuIGJlIGNvbnNpZGVyZWQgY2F0YWx5dGljYWxseSBpbmFjdGl2ZS4KCkluIGEgZmlyc3Qgc3RlcCwgYWxsIGRhdGEgb2YgcmVsZXZhbmNlIHdpbGwgYmUgbG9hZGVkLgoKYGBge3IgbG9hZC1yZXF1aXJlZC1kYXRhc2V0cywgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KY291bnRfbWF0cml4IDwtIHJlYWRfdHN2KCIuLi9yZXN1bHRzL3ByZXBhcmUvYWxsX2NvdW50cy50c3YiKQpjb3VudF9tYXRyaXgkR2VuZSA8LSBOVUxMCmZpdG5lc3NfZGF0YSA8LSAgcmVhZF90c3YoIi4uL1dlaWdodGluZ1N0cmF0ZWdpZXNfZml0bmVzc0NhbGMvcmVzdWx0X2xmY1NFX1BlYXJzb25fV2VpZ2h0ZWRfc2FtZVdlaWdodC50c3YiKQpzYXR1X2NvbWJpIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC9jb21iaV9zYXR1cl9tdXRhdGlvcy50c3YiLCBjb2xfbmFtZXMgPSBjKCJzZ1JOQV90YXJnZXQiLCAiY2F0ZWdvcnkiKSkKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIHNhdHVfY29tYmkpCm51bWJlcl9tdXRzIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC9udW1iZXJfbXV0YXRpb25zX3ZhcmlhbnRzLnRzdiIpCmZpdG5lc3NfZGF0YSA8LSBsZWZ0X2pvaW4oZml0bmVzc19kYXRhLCBudW1iZXJfbXV0cykKCmRmX3NhbXBsZXNoZWV0IDwtIHJlYWRyOjpyZWFkX2NzdigiLi4vaW5wdXQvc2FtcGxlc2hlZXRfMXN0Q3VsdGl2XzJuZEN1bHRpdi5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQogICAgc2VsZWN0KGFsbF9vZihjKCJzYW1wbGUiLCAiY29uZGl0aW9uIiwgInJlcGxpY2F0ZSIsICJ0aW1lIiwgImdyb3VwIiwgInJlZmVyZW5jZV9ncm91cCIpKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGdyb3VwID0gZmFjdG9yKGBncm91cGApKQpkZl9zYW1wbGVzaGVldCRuYW1lIDwtIHBhc3RlKCJnZW4sIiwgZGZfc2FtcGxlc2hlZXQkdGltZSwgIixyLCIsIGRmX3NhbXBsZXNoZWV0JHJlcGxpY2F0ZSwgIixjb25kLCIsIGRmX3NhbXBsZXNoZWV0JGNvbmRpdGlvbiwgc2VwPSIiKQoKdHVubmVsX211dGF0aW9ucyA8LSByZWFkX3RzdigiLi4vaW5wdXQvbGlzdF90dW5uZWxfbXV0YXRpb25zX3dvT3ZlcmxhcERlZXBTZXF1ZW5jZS50eHQiLCBjb2xfbmFtZXMgPSBjKCJub05TdHJlcCIsICJhYV9wb3Nfd2l0aFN0cmVwIikpCmZpdG5lc3NfZGF0YSRiYXNlQUEgPC0gc3Vic3RyKGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQsIDEsIG5jaGFyKGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQpLTEpCmZpdG5lc3NfZGF0YSA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCAhZml0bmVzc19kYXRhJGJhc2VBQSAlaW4lIHR1bm5lbF9tdXRhdGlvbnMkYWFfcG9zX3dpdGhTdHJlcCkKCkVWY291cGxpbmdzX3ByZWRpY3QgPC0gcmVhZF90c3YoIi4uL2luc2lsaWNvX3ByZWRpY3Rpb25zL0VWY291cGxpbmdzL3ZhcmlhbnRzX3Njb3JlZF9FVmNvdXBsaW5ncy50c3YiKQpuYW1lcyhFVmNvdXBsaW5nc19wcmVkaWN0KSA8LSBjKCJzZ1JOQV90YXJnZXQiLCAiRVZjb3VwX3ByZWRpY3QiKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgRVZjb3VwbGluZ3NfcHJlZGljdCkKCiNEZWVwU2VxX3ByZWRpY3Rfc2luZ2xlIDwtIHJlYWRfZGVsaW0oIi4uL2luc2lsaWNvX3ByZWRpY3Rpb25zL0RlZXBTZXF1ZW5jZS8yMDIzLTA0LTIwX3NpbmdsZV9tdXRhbnRfbWF0cml4X0dhbGxpb25lbGxhX1J1YmlzY29fSV9zYW1wbGVzLTEwX2VsYm9fcHJlZGljdGlvbnMuY3N2IiwgY29sX25hbWVzID0gYygic2dSTkFfdGFyZ2V0IiwgIkRlZXBTZXFfcHJlZGljdF9zaW5nbGUiKSwgZGVsaW09IjsiKQojZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIERlZXBTZXFfcHJlZGljdCkKCkRlZXBTZXFfcHJlZGljdF9jb21wbGV0ZSA8LSByZWFkX3RzdigiLi4vaW5zaWxpY29fcHJlZGljdGlvbnMvRGVlcFNlcXVlbmNlLzIwMjQtMDctMzBfdmFyaWFudHNfc2NvcmVkX0RlZXBTZXF1ZW5jZS50c3YiLCBjb2xfbmFtZXMgPSBjKCJzZ1JOQV90YXJnZXQiLCAiRGVlcFNlcV9wcmVkaWN0IikpCmZpdG5lc3NfZGF0YSA8LSBsZWZ0X2pvaW4oZml0bmVzc19kYXRhLCBEZWVwU2VxX3ByZWRpY3RfY29tcGxldGUpCgojIHJ1biBhZGRpdGl2ZU1vZGVsLnB5IGlmIHJlLWFuYWx5c2luZwphZGRpdGl2ZV9zY29yZXNfQ0xfTjIgPC0gcmVhZF90c3YoIi4uL2luc2lsaWNvX3ByZWRpY3Rpb25zL2FkZGl0aXZlTW9kZWwvcmVzdWx0cy9jb21iaW5hdG9yaWFsX3ZhcmlhbnRzX2FkZGl0aXZlU2NvcmVzX0NMX04yLmNzdiIpWyxjKDIsNCldCmFkZGl0aXZlX3Njb3Jlc19DTF9OMiRjb25kaXRpb24gPC0gIkNMX04yIgphZGRpdGl2ZV9zY29yZXNfQ0xfTzIgPC0gcmVhZF90c3YoIi4uL2luc2lsaWNvX3ByZWRpY3Rpb25zL2FkZGl0aXZlTW9kZWwvcmVzdWx0cy9jb21iaW5hdG9yaWFsX3ZhcmlhbnRzX2FkZGl0aXZlU2NvcmVzX0NMX08yLmNzdiIpWyxjKDIsNCldCmFkZGl0aXZlX3Njb3Jlc19DTF9PMiRjb25kaXRpb24gPC0gIkNMX08yIgphZGRpdGl2ZV9zY29yZXNfTEQgPC0gcmVhZF90c3YoIi4uL2luc2lsaWNvX3ByZWRpY3Rpb25zL2FkZGl0aXZlTW9kZWwvcmVzdWx0cy9jb21iaW5hdG9yaWFsX3ZhcmlhbnRzX2FkZGl0aXZlU2NvcmVzX0xELmNzdiIpWyxjKDIsNCldCmFkZGl0aXZlX3Njb3Jlc19MRCRjb25kaXRpb24gPC0gIkxEIgphZGRpdGl2ZV9zY29yZXMgPC0gYmluZF9yb3dzKGFkZGl0aXZlX3Njb3Jlc19DTF9OMiwgYWRkaXRpdmVfc2NvcmVzX0NMX08yLCBhZGRpdGl2ZV9zY29yZXNfTEQpCmZpdG5lc3NfZGF0YSA8LSBsZWZ0X2pvaW4oZml0bmVzc19kYXRhLCBhZGRpdGl2ZV9zY29yZXMpCgpjb25zZXJ2YXRpb25fc2NvcmVzIDwtIHJlYWRfY3N2KCIuLi9pbnB1dC9FVmNvdXBsaW5nc19EZWVwU2VxdS9UQVJHRVRfYjAuM19zaW5nbGVfbXV0YW50X21hdHJpeC5jc3YiKVssYygyLDcpXQpuYW1lcyhjb25zZXJ2YXRpb25fc2NvcmVzKSA8LSBjKCJzZ1JOQV90YXJnZXQiLCAiY29uc2VydmF0aW9uU2NvcmUiKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgY29uc2VydmF0aW9uX3Njb3JlcykKCnJlbFNBU19zY29yZXMgPC0gcmVhZF90c3YoIi4uL2lucHV0L3JlbFNBU19HYWxsaW9uZWxsYV9DYmJNLUkudHN2IikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIHJlbFNBU19zY29yZXMpCgpkaW1lciA8LSByZWFkX3RzdigiLi4vaW5wdXQvZGltZXJfYWEudHN2IikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIGRpbWVyKQoKIyBnZXQgTVNBIFRyYW5zZm9ybWVyIGVuc2VtYmxlIHByZWRpY3Rpb25zIGZyb20gUHJvdGVpbk5QVCwgcnVuIGNoYW5nZV9tdXRhbnRfbmFtZV9DQkJNX1RSQUlOLnB5IG9uIGZpbGUgdG8gZ2V0IG9ubHkgcHJlZGljdGlvbnMgYW5kIG11dGFudCBuYW1lLCBsb2FkIGFuZCBtZXJnZQpNU0F0cmFuc2YgPC0gcmVhZF90c3YoIi4uL2lucHV0L0NCQk1fVFJBSU5fb25seV9wcmVkaWN0QW5kTXV0YW50LnRzdiIpCmZpdG5lc3NfZGF0YSA8LSBsZWZ0X2pvaW4oZml0bmVzc19kYXRhLCBNU0F0cmFuc2YpCgpwcm90ZWluTlBUIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC9Qcm90ZWluTlBUX211dGFudF9maXRuZXNzUHJlZGljdGlvbnMudHN2IikKcHJvdGVpbk5QVCA8LSBwaXZvdF9sb25nZXIocHJvdGVpbk5QVCwgY29scz1jKDIsMyw0KSwgbmFtZXNfdG89ImNvbmRpdGlvbiIsIHZhbHVlc190bz0icHJvdGVpbk5QVF9wcmVkaWN0IikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIHByb3RlaW5OUFQpCmBgYAoKIyBEaWFnbm9zdGljIHBsb3RzIGZvciBjdWx0aXZhdGlvbgoKIyMgU2FtcGxlLXNhbXBsZSBjb3JyZWxhdGlvbgoKV2hlbiBjbHVzdGVyaW5nIGFjY29yZGluZyB0byBzaW1pbGFyaXR5LCByZXBsaWNhdGVzIGZyb20gdGhlIHNhbWUgZ2FzIGNvbmRpdGlvbnMgaGF2ZSBhIHRlbmRlbmN5IHRvIGNsdXN0ZXIgdG9nZXRoZXIsIGlycmVzcGVjdGl2ZSBvZiB0aGVpciBsaWdodCBjb25kaXRpb24gKGNvbnN0YW50IGxpZ2h0IG9yIGxpZ2h0LWRhcmspLiBUaGlzIGlzIGFsc28gdGhlIGNhc2UgYXQgZ2VuZXJhdGlvbiAwIC0gZXZlbiBhdCBnZW5lcmF0aW9uIDAsIHNhbXBsZXMgYXJlIG1vcmUgc2ltaWxhciB0byBvdGhlciBzYW1wbGVzIGZyb20gdGhlIHNhbWUgZ2FzIGZlZWQgdGhhbiB0byBnZW5lcmF0aW9uIDAgc2FtcGxlcyBvZiB0aGUgb3RoZXIgcm91bmQgb2YgY3VsdGl2YXRpb24gKGZpcnN0IHJvdW5kIG9mIGN1bHRpdmF0aW9uIHdhcyBDTF9OMjsgQ0xfTzIgYW5kIExEIHdlcmUgcnVuIGluIGEgc2Vjb25kIHJvdW5kIG9mIGN1bHRpdmF0aW9ucykuCgpUaGVyZSBpcyBzb21lIGNsZWFyIHNlcGFyYXRpb24gYmV0d2VlbiBzYW1wbGVzIGZyb20gZWFybGllciB0aW1lIHBvaW50cyBhbmQgc2FtcGxlcyBmcm9tIGxhdGVyIHRpbWUgcG9pbnRzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTAsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KZGZfY291bnRzIDwtIHRpZHlyOjpwaXZvdF9sb25nZXIoY291bnRfbWF0cml4LAogICAgY29scyA9IDI6bmNvbChjb3VudF9tYXRyaXgpLAogICAgbmFtZXNfdG8gPSAic2FtcGxlIiwgdmFsdWVzX3RvID0gIm5fcmVhZHMiCikKCiMgc29ydApkZl9jb3VudHMgPC0gYXJyYW5nZShkZl9jb3VudHMsIHNhbXBsZSkKZGZfY291bnRzIDwtIGxlZnRfam9pbihkZl9zYW1wbGVzaGVldCwgZGZfY291bnRzKQoKZGZfY29ycmVsYXRpb24gPC0gZGZfY291bnRzWyxjKCJuYW1lIiwgInNnUk5BIiwgIm5fcmVhZHMiKV0gJT4lCiAgICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJuYW1lIiwgdmFsdWVzX2Zyb20gPSAibl9yZWFkcyIpICU+JQogICAgZHBseXI6OnNlbGVjdCgtYygxKSkgJT4lCiAgICBjb3IoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEwLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmFubm90YXRpb25fZGF5cyA9IGRhdGEuZnJhbWUocm93Lm5hbWVzPXVuaXF1ZShyb3cubmFtZXMoZGZfY29ycmVsYXRpb24pKSwgZ2VuZXJhdGlvbj1hcy5jaGFyYWN0ZXIoYyhyZXAoYygiMCIsICIzLjciLCAiOC42IiwgIjEwLjEiKSwgMiksIHJlcChjKCIxMC4xIiwgIjguNiIsICIzLjciLCAiMCIpLCAyKSwgcmVwKGMoIjMuNyIsICIxMC4xIiwgIjAiLCAiOC42IiksIDIpLCAiOC42IiwgIjAiLCAiMTAuMSIsICIzLjciLCAiMy43IiwgIjAiLCAiMCIsICI1LjQiLCAiNi40IiwgIjcuNyIsICIxMC4zIiwgIjAiLCAiNS40IiwgIjAiLCAiNS40IiwgcmVwKGMoIjAiLCAiNS40IiwgIjYuNCIsICI3LjciLCAiMTAuMyIpLCA1KSwgcmVwKGMoIjAiLCAiNC45IiwgIjguNiIsICIxMi41IiksOCkpKSwgY29uZGl0aW9uPWMocmVwKCJDTF9OMiIsIDMwKSwgcmVwKCJMRCIsIDM0KSwgcmVwKCJDTF9PMiIsIDMyKSksIHJlcGxpY2F0ZT1hcy5jaGFyYWN0ZXIoYyhyZXAoIjEiLCA0KSwgcmVwKCIyIiwgNCksIHJlcCgiMyIsIDQpLCByZXAoIjQiLCA0KSwgcmVwKCI1IiwgNCksIHJlcCgiNiIsIDQpLCByZXAoIjciLCA0KSwgcmVwKCI4IiwgMiksIHJlcCgiMSIsIDUpLCByZXAoIjIiLCAyKSwgcmVwKCIzIiwgMiksIHJlcCgiNCIsIDUpLCByZXAoIjUiLCA1KSwgcmVwKCI2IiwgNSksIHJlcCgiNyIsIDUpLCByZXAoIjgiLCA1KSwgcmVwKCIxIiwgNCksIHJlcCgiMiIsIDQpLCByZXAoIjMiLCA0KSwgcmVwKCI0IiwgNCksIHJlcCgiNSIsIDQpLCByZXAoIjYiLCA0KSwgcmVwKCI3IiwgNCksIHJlcCgiOCIsIDQpKSksIGdhc19mZWVkPWMocmVwKCJOMiIsIDMwKSwgcmVwKCJPMiIsIDY2KSkpCgojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQxNjI4NDUwL3ItcGhlYXRtYXAtY2hhbmdlLWFubm90YXRpb24tY29sb3JzLWFuZC1wcmV2ZW50LWdyYXBoaWNzLXdpbmRvdy1mcm9tLXBvcHBpbmctdXAKIyBjaG9vc2UgZ3JhZGllbnQgb2YgY29sb3JzIGZvciBnZW5lcmF0aW9ucwojIGUuZy4gVG9sIGZyb20gaHR0cHM6Ly9kYXZpZG1hdGhsb2dpYy5jb20vY29sb3JibGluZC8jJTIzRDgxQjYwLSUyMzFFODhFNS0lMjNGRkMxMDctJTIzMDA0RDQwCm9rYWJlIDwtIGMoIiNmMGU0NDJmZiIsICIjZTY5ZjAwZmYiLCAiI2Q1NWUwMGZmIiwgIiNjYzc5YTdmZiIsICIjMDA5ZTczZmYiLCAiIzU2YjRlOWZmIiwgIiMwMDcyYjJmZiIsICIjYWFhYWFhZmYiKQpnZW5lcmF0aW9ucyA8LSBva2FiZVtjKDEsIDIsIDMsIDMsIDQsIDQsIDQsIDUsIDUsIDUpXQojIGdlbmVyYXRpb25zICIwIiAgICAiMy43IiAgIjguNiIgICIxMC4xIiAiNS40IiAgIjYuNCIgICI3LjciICAiMTAuMyIgIjQuOSIgICIxMi41IgpuYW1lcyhnZW5lcmF0aW9ucykgPC0gYygiMCIsICIzLjciLCAiNC45IiwiNS40IiwgIjYuNCIsICI3LjciLCAiOC42IiwgIjEwLjEiLCAiMTAuMyIsICIxMi41IikKIyByZXBsaWNhdGVzIDEgdG8gOApyZXAgPC0gb2thYmVbMTo4XQpuYW1lcyhyZXApIDwtIGFzLmNoYXJhY3RlcigxOjgpCmZlZWQgPC0gb2thYmVbYygyLCA3KV0KbmFtZXMoZmVlZCkgPC0gYygiTjIiLCAiTzIiKQphbm5vdGF0aW9uX2NvbG9yX2xpc3QgPC0gbGlzdChjb25kaXRpb249Y29sX2NvbmRpdGlvbnMsIHJlcGxpY2F0ZT1yZXAsIGdlbmVyYXRpb249Z2VuZXJhdGlvbnMsIGdhc19mZWVkPWZlZWQpCgojIGh0dHBzOi8vYmlvaW5mb3JtYXRpY3Muc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzIyNTAyL21hbnVhbGx5LXNldC1yYW5nZS1vZi1jb2xvdXItc2NhbGUtaW4tcGhlYXRtYXAtaW4tcgpjb2xvci5kaXZpc2lvbnMgPC0gMTAwCgpwIDwtIHBoZWF0bWFwKGRmX2NvcnJlbGF0aW9uLCBkaXNwbGF5X251bWJlcnM9VFJVRSwgdHJlZWhlaWdodF9jb2w9MCwgY3V0cmVlX3Jvd3MgPSA2LCBjdXRyZWVfY29scyA9IDYsIGFubm90YXRpb25fcm93ID0gYW5ub3RhdGlvbl9kYXlzLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25fY29sb3JfbGlzdCwgYnJlYWtzID0gc2VxKDAsMSwgbGVuZ3RoLm91dD0oY29sb3IuZGl2aXNpb25zICsgMSkpLCBmb250c2l6ZT02KQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvY29ycmVsYXRpb25fc2FtcGxlc19jbHVzdGVyaW5nX251bWJlcnMucGRmIiwgcGxvdD1wLCB3aWR0aD0yMCwgaGVpZ2h0PTIwKQoKcCA8LSBwaGVhdG1hcChkZl9jb3JyZWxhdGlvbiwgZGlzcGxheV9udW1iZXJzPUZBTFNFLCB0cmVlaGVpZ2h0X2NvbD0wLCBjdXRyZWVfcm93cyA9IDYsIGN1dHJlZV9jb2xzID0gNiwgYW5ub3RhdGlvbl9yb3cgPSBhbm5vdGF0aW9uX2RheXMsIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbl9jb2xvcl9saXN0LCBicmVha3MgPSBzZXEoMCwxLCBsZW5ndGgub3V0PShjb2xvci5kaXZpc2lvbnMgKyAxKSksIGZvbnRzaXplPTYpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL2NvcnJlbGF0aW9uX3NhbXBsZXNfY2x1c3RlcmluZy5wbmciLCBwbG90PXAsIHdpZHRoPTExLjUsIGhlaWdodD0xMCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2NvcnJlbGF0aW9uX3NhbXBsZXNfY2x1c3RlcmluZy5wZGYiLCBwbG90PXAsIHdpZHRoPTExLjUsIGhlaWdodD0xMCkKYGBgCgojIyBDaGVjayBlcXVhbCBkaXN0cmlidXRpb24gaW4gdGltZSAwIHNhbXBsZXMKCkZvciBjaGVja2luZyB0aGUgZGlzdHJpYnV0aW9uIGF0IHRpbWUgcG9pbnQgMCwgdHdvIHQgPSAwIHJlcGxpY2F0ZXMgZnJvbSBlYWNoIGNvbmRpdGlvbiB3ZXJlIHVzZWQuIAoKYGBge3IgcHJlcGFyZS1jb3VudC1tYXRyaXgtREVTZXEyfQpjb3VudF9tYXRyaXhfdGltZTAgPC0gYXMuZGF0YS5mcmFtZShkYXRhLmZyYW1lKHNnUk5BPWNvdW50X21hdHJpeCRzZ1JOQSwgTERfMT1jb3VudF9tYXRyaXgkTEQxQTEsIExEXzI9Y291bnRfbWF0cml4JExEMUE2LCBPMl8xPWNvdW50X21hdHJpeCRPMjJBMSwgTzJfMj1jb3VudF9tYXRyaXgkTzIyQTUsIE4yXzE9Y291bnRfbWF0cml4JGhpZ2hONEExLCBOMl8yPWNvdW50X21hdHJpeCRoaWdoTjRBNSkpCgpyb3cubmFtZXMoY291bnRfbWF0cml4X3RpbWUwKSA8LSBjb3VudF9tYXRyaXhfdGltZTAkc2dSTkEKY291bnRfbWF0cml4X3RpbWUwJHNnUk5BIDwtIE5VTEwKCmRlc2lnbl9tYXRyaXggPC0gZGF0YS5mcmFtZShncm91cD1yZXAoInQwIiwgNikpCnJvdy5uYW1lcyhkZXNpZ25fbWF0cml4KSA9IG5hbWVzKGNvdW50X21hdHJpeF90aW1lMCkKYGBgCgpERVNlcTIgaXMgdXNlZCB0byBub3JtYWxpemUgYW1vbmcgdGhlIHJlcGxpY2F0ZXMuCgpgYGB7ciBub3JtYWxpemUtdXNpbmctREVTZXEyLCBtZXNzYWdlPUZBTFNFfQpkZHN0aW1lMCA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KAogIGNvdW50RGF0YSA9IGNvdW50X21hdHJpeF90aW1lMCwKICBjb2xEYXRhID0gZGVzaWduX21hdHJpeCwKICBkZXNpZ24gPSB+IDEpCmRkc3RpbWUwIDwtIGVzdGltYXRlU2l6ZUZhY3RvcnMoZGRzdGltZTApCmNvdW50c19ub3JtX2Rkc3RpbWUwIDwtIGFzLmRhdGEuZnJhbWUoY291bnRzKGRkc3RpbWUwLCBub3JtYWxpemVkPVRSVUUpKQpgYGAKCldoZW4gbG9nMTAtdHJhbnNmb3JtaW5nIHRoZSB4LXNjYWxlIG9mIHRoZSBtZWFuIG9mIGNvdW50cyBpbiB0aGUgZGlmZmVyZW50IHNhbXBsZXMsIHRoZSBkaXN0cmlidXRpb24gbG9va3MgYXMgaWYgbWVhbiB2YWx1ZXMgd2VyZSBhbG1vc3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFRoaXMgaXMsIGlmIEkgYW0gbm90IG1pc3Rha2VuLCByZWxhdGl2ZWx5IHR5cGljYWwgZm9yIGNvdW50IGRhdGEuIChDb3VudCBkYXRhIGRvZXMgdXN1YWxseSBmb2xsb3cgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbiwgd2hpY2ggbG9va3MgbW9yZSAibm9ybWFsIiB3aGVuIGJlaW5nIGxvZy10cmFuc2Zvcm1lZCkKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpkZl9wbG90IDwtIGRhdGEuZnJhbWUobXV0PXJvdy5uYW1lcyhjb3VudHNfbm9ybV9kZHN0aW1lMCksIG1lYW5fY291bnQ9YXBwbHkoY291bnRzX25vcm1fZGRzdGltZTAsIDEsIG1lYW4pKQpwIDwtIGdncGxvdChkZl9wbG90LCBhZXMoeD1tZWFuX2NvdW50KSkgKyBnZW9tX2hpc3RvZ3JhbSgpICsgdGhlbWVfbGlnaHQoKSArIHNjYWxlX3hfbG9nMTAoKQpwCmBgYAoKVGhlIHBsb3QgYmVsb3cgc2hvd3MgbWVhbiBhbmQgc3RkZXYgb2YgdGhlIGNvdW50IG9mIDEwMCByYW5kb21seSBwaWNrZWQgYmFyY29kZXMgcHJlc2VudCBpbiB0aGUgc2FtcGxlcyBhdCB0aW1lIHBvaW50IDAgYW5kIGNvbmZpcm1zIHRoZSBoaXN0b2dyYW0gZnJvbSBhYm92ZS4gVGhlcmUgYXJlIG91dGxpZXJzIHdpdGggbWFueSBjb3VudHMgb3IgYWxtb3N0IG5vIGNvdW50cyBhbmQgYSBodWdlIGJ1bGsgb2YgYmFyY29kZXMgd2l0aCBhIHNpbWlsYXIgY291bnQuIEFjY29yZGluZyB0byB0aGUgaGlzdG9ncmFtIGFib3ZlLCB0aGlzIGJ1bGsgaXMgYXBwcm94LiBhdCBhIG1lYW4gY291bnQgb2YgMTAuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpkZl9wbG90IDwtIGNvdW50c19ub3JtX2Rkc3RpbWUwW3JvdW5kKHJ1bmlmKG49MTAwLCBtaW49MSwgbWF4PW5yb3coY291bnRzX25vcm1fZGRzdGltZTApKSksXQpkZl9wbG90IDwtIGRhdGEuZnJhbWUobXV0PXJvdy5uYW1lcyhkZl9wbG90KSwgbWVhbl9jb3VudD1hcHBseShkZl9wbG90LCAxLCBtZWFuKSwgc3RkZXY9YXBwbHkoZGZfcGxvdCwxLHNkKSkKcCA8LSBnZ3Bsb3QoZGZfcGxvdCkgKyBnZW9tX2JhcihhZXMoeD1yZW9yZGVyKG11dCwgbWVhbl9jb3VudCksIHk9bWVhbl9jb3VudCksIHN0YXQ9ImlkZW50aXR5IikgKyBnZW9tX2Vycm9yYmFyKGFlcyh4PW11dCwgeW1pbj1tZWFuX2NvdW50LXN0ZGV2LCB5bWF4PW1lYW5fY291bnQrc3RkZXYpLCB3aWR0aD0wLjQpICsgdGhlbWVfbGlnaHQoKSArIHhsYWIoIjEwMCByYW5kb21seSBwaWNrZWQgYmFyY29kZXMiKSArIHlsYWIoIk1lYW4gb2Ygbm9ybWFsaXplZCByZWFkIGNvdW50cyIpICsgbGFicyh0aXRsZT0iUmFuZG9tbHkgcGlja2VkIGJhcmNvZGVzIGFuZCB0aGVpciBhdmVyYWdlIGFidW5kYW5jZSIpICsgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi90aW1lMF9kaXN0cmlidXRpb24ucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvdGltZTBfZGlzdHJpYnV0aW9uLnBuZyIsIHBsb3Q9cCkKYGBgCgpDYWxjdWxhdGUgdGhlIEdpbmkgaW5kZXggb2YgYSBmZXcgb2YgdGhlIHNhbXBsZXMsIGEgdmFsdWUgb2YgMCByZWZsZWN0cyBjb21wbGV0ZSBlcXVhbGl0eSwgYSB2YWx1ZSBvZiAxIG1heGltYWwgaW5lcXVhbGl0eS4gR2luaSBpbmRpY2VzIGZvciBhbGwgc2FtcGxlcyBhcmUgYWxzbyBjYWxjdWxhdGVkIGFzIHBhcnQgb2YgdGhlIHdob2xlIG5mLWNyaXNwcmlzY3JlZW4gcGlwZWxpbmUuIFdpdGggdmFsdWVzIG9mIGFwcHJveC4gMC41NSB0byAwLjYsIGJhcmNvZGVzIGF0IHRpbWUgcG9pbnQgMCBhcmUgbm90IGNvbXBsZXRlbHkgaW5lcXVhbGx5IGRpc3RyaWJ1dGVkLCBidXQgYWxzbyBmYXIgZnJvbSBwZXJmZWN0IGVxdWFsaXR5LgoKYGBge3IgR2luaS1pbmRleH0KR2luaShjb3VudHNfbm9ybV9kZHN0aW1lMCRMRF8xLCB1bmJpYXNlZD1GQUxTRSkKR2luaShjb3VudHNfbm9ybV9kZHN0aW1lMCRMRF8yLCB1bmJpYXNlZD1GQUxTRSkKR2luaShjb3VudHNfbm9ybV9kZHN0aW1lMCRPMl8xLCB1bmJpYXNlZD1GQUxTRSkKR2luaShjb3VudHNfbm9ybV9kZHN0aW1lMCRPMl8yLCB1bmJpYXNlZD1GQUxTRSkKR2luaShjb3VudHNfbm9ybV9kZHN0aW1lMCROMl8xLCB1bmJpYXNlZD1GQUxTRSkKR2luaShjb3VudHNfbm9ybV9kZHN0aW1lMCROMl8yLCB1bmJpYXNlZD1GQUxTRSkKR2luaShkZl9wbG90JG1lYW5fY291bnQsIHVuYmlhc2VkPUZBTFNFKQpgYGAKCiMgRXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcwoKIyMgT3ZlcnZpZXcgZml0bmVzcyBkaXN0cmlidXRpb24KCk5vcm1hbGl6YXRpb246IFdUIGlzIHNldCB0byAiMSIsIGFuZCB0aGUgbWVkaWFuIG9mIEsyMTQgc3Vic3RpdHV0aW9ucyBhcyAiMCIuIEZvciBhbGwgdGhyZWUgY29uZGl0aW9ucywgdGhlIGRhdGEgc2V0IHNob3dzIGEgYmltb2RhbCBkaXN0cmlidXRpb24uIFRoaXMgaXMgbW9zdCBwcm9ub3VuY2VkIGZvciB0aGUgY29udGludW91cyBsaWdodCwgTjIgZmVlZCBjb25kaXRpb24uCgojIyMgQ29tcGxldGUgbGlicmFyeQoKYGBge3IgbWVzc2FnZT1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gdW5pcXVlKGZpdG5lc3NfZGF0YVssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAicF9maXRfYWRqX1dUIiwgImNvbmRpdGlvbiIpXSkKCnAgPC0gZ2dwbG90KHBsb3Rfc3Vic2V0LCBhZXMoeD1ub3JtLCBncm91cD1jb25kaXRpb24sIGZpbGw9Y29uZGl0aW9uLCBjb2xvcj1jb25kaXRpb24sIGxpbmV0eXBlPWNvbmRpdGlvbikpICsgZ2VvbV9kZW5zaXR5KGFkanVzdD0xLjUsIGFscGhhPS40KSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiQ0xfTjIiPSJzb2xpZCIsICJDTF9PMiI9ImRhc2hlZCIsICJMRCI9ImRvdHRlZCIpLCBsYWJlbHM9YygiQ29udGludW91cyBsaWdodCwgTjIgZmVlZCIsICJDb250aW51b3VzIGxpZ2h0LCBPMiBmZWVkIiwgIkxpZ2h0LWRhcmsgY3ljbGVzLCBPMiBmZWVkIikpICsgbGFicyh0aXRsZT0iQ29tcGxldGUgQ2JiTSBsaWJyYXJ5IiwgeD0iTm9ybWFsaXplZCBmaXRuZXNzIHZhbHVlIikgCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3dob2xlX2xpYnJhcnlfZGVuc2l0eXBsb3QucGRmIiwgcGxvdD1wLCB3aWR0aD0xMS41LCBoZWlnaHQ9OCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dob2xlX2xpYnJhcnlfZGVuc2l0eXBsb3QucG5nIiwgcGxvdD1wLCB3aWR0aD0xMS41LCBoZWlnaHQ9OCkKYGBgCgpgYGB7cn0KZm9yKGNvbmQgaW4gdW5pcXVlKHBsb3Rfc3Vic2V0JGNvbmRpdGlvbikpewogIHByaW50KGNvbmQpCiAgdGVtcF9zdWJzIDwtIHN1YnNldChwbG90X3N1YnNldCwgcGxvdF9zdWJzZXQkY29uZGl0aW9uPT1jb25kKQogIHByaW50KG5yb3codGVtcF9zdWJzKSkKICBwcmludCgiQWJvdmUgMSIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA+IDEpKSkKICBwcmludCgiQWJvdmUgMSwgcGVyY2VudGFnZSIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA+IDEpKS9ucm93KHRlbXBfc3VicykpCiAgcHJpbnQoIkFib3ZlIDEgYW5kIHNpZ25pZmljYW50bHkgaGlnaGVyIHRoYW4gV1QiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxICYgdGVtcF9zdWJzJHBfZml0X2Fkal9XVCA8IDAuMDUpKSkKICBwcmludCgiQWJvdmUgMSBhbmQgc2lnbmlmaWNhbnRseSBoaWdoZXIgdGhhbiBXVCwgcGVyY2VudGFnZSIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA+IDEgJiB0ZW1wX3N1YnMkcF9maXRfYWRqX1dUIDwgMC4wNSkpL25yb3codGVtcF9zdWJzKSkKICBwcmludCgiQmVsb3cgMCIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA8IDApKSkKICBwcmludCgiQmVsb3cgMCwgcGVyY2VudGFnZSIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA8IDApKS9ucm93KHRlbXBfc3VicykpCn0KYGBgCgojIyMgQ29tYmluYXRvcmlhbCBsaWJyYXJ5CgpXaGVuIG9ubHkgY2hlY2tpbmcgbXV0YW50IHZhcmlhbnRzIHByZXNlbnQgaW4gdGhlIGNvbWJpbmF0b3JpYWwgbGlicmFyeSB3aXRoIG1vcmUgdGhhbiBhIHNpbmdsZSBhbWlubyBhY2lkIGV4Y2hhbmdlLCB3ZSBzdGlsbCBvYnNlcnZlIGEgY2xlYXIgYmltb2RhbCBkaXN0cmlidXRpb24gdmVyeSBzaW1pbGFyIHRvIHRoZSBvbmUgb2YgdGhlIGNvbXBsZXRlIGRhdGEgc2V0LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBsb3Rfc3Vic2V0IDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCAoZml0bmVzc19kYXRhJGNhdGVnb3J5ICVpbiUgYygiY29tYmluYXRvcmlhbCIpKSAmIGZpdG5lc3NfZGF0YSRudW1iZXJfbXV0cyA+IDEpWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJwX2ZpdF9hZGpfV1QiLCAiY29uZGl0aW9uIildKQpsZW5ndGgodW5pcXVlKHBsb3Rfc3Vic2V0JHNnUk5BX3RhcmdldCkpCmxlbmd0aCh1bmlxdWUocGxvdF9zdWJzZXQkc2dSTkFfdGFyZ2V0KSkvbGVuZ3RoKHVuaXF1ZShmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0KSkKCnAgPC0gZ2dwbG90KHBsb3Rfc3Vic2V0LCBhZXMoeD1ub3JtLCBncm91cD1jb25kaXRpb24sIGZpbGw9Y29uZGl0aW9uLCBjb2xvcj1jb25kaXRpb24sIGxpbmV0eXBlPWNvbmRpdGlvbikpICsgZ2VvbV9kZW5zaXR5KGFkanVzdD0xLjUsIGFscGhhPS40KSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoIkNMX04yIj0ic29saWQiLCAiQ0xfTzIiPSJkYXNoZWQiLCAiTEQiPSJkb3R0ZWQiKSwgbGFiZWxzPWMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xfY29uZGl0aW9ucywgbGFiZWxzID0gYygiQ29udGludW91cyBsaWdodCwgTjIgZmVlZCIsICJDb250aW51b3VzIGxpZ2h0LCBPMiBmZWVkIiwgIkxpZ2h0LWRhcmsgY3ljbGVzLCBPMiBmZWVkIikpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29uZGl0aW9ucywgbGFiZWxzID0gYygiQ29udGludW91cyBsaWdodCwgTjIgZmVlZCIsICJDb250aW51b3VzIGxpZ2h0LCBPMiBmZWVkIiwgIkxpZ2h0LWRhcmsgY3ljbGVzLCBPMiBmZWVkIikpICsgbGFicyh0aXRsZT0iQ29tYmluYXRvcmlhbCBDYmJNIGxpYnJhcnkgd2l0aCBtb3JlIHRoYW4gb25lIGV4Y2hhbmdlIiwgeD0iTm9ybWFsaXplZCBmaXRuZXNzIHZhbHVlIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvY29tYmluYXRvcmlhbF93LW9TaW5nbGVzX2xpYnJhcnlfZGVuc2l0eXBsb3QucGRmIiwgcGxvdD1wLCB3aWR0aD0xMS41LCBoZWlnaHQ9OCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2NvbWJpbmF0b3JpYWxfdy1vU2luZ2xlc19saWJyYXJ5X2RlbnNpdHlwbG90LnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTgpCmBgYAoKYGBge3J9CmZvcihjb25kIGluIHVuaXF1ZShwbG90X3N1YnNldCRjb25kaXRpb24pKXsKICBwcmludChjb25kKQogIHRlbXBfc3VicyA8LSBzdWJzZXQocGxvdF9zdWJzZXQsIHBsb3Rfc3Vic2V0JGNvbmRpdGlvbj09Y29uZCkKICBwcmludChucm93KHRlbXBfc3VicykpCiAgcHJpbnQoIkFib3ZlIDEiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxKSkpCiAgcHJpbnQoIkFib3ZlIDEsIHBlcmNlbnRhZ2UiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxKSkvbnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJBYm92ZSAxIGFuZCBzaWduaWZpY2FudGx5IGhpZ2hlciB0aGFuIFdUIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSAmIHRlbXBfc3VicyRwX2ZpdF9hZGpfV1QgPCAwLjA1KSkpCiAgcHJpbnQoIkFib3ZlIDEgYW5kIHNpZ25pZmljYW50bHkgaGlnaGVyIHRoYW4gV1QsIHBlcmNlbnRhZ2UiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxICYgdGVtcF9zdWJzJHBfZml0X2Fkal9XVCA8IDAuMDUpKS9ucm93KHRlbXBfc3VicykpCiAgcHJpbnQoIkJlbG93IDAiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPCAwKSkpCiAgcHJpbnQoIkJlbG93IDAsIHBlcmNlbnRhZ2UiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPCAwKSkvbnJvdyh0ZW1wX3N1YnMpKQp9CmBgYAoKIyMjIFNhdHVyYXRpb25hbCBsaWJyYXJ5CgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpwbG90X3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJGNhdGVnb3J5ICVpbiUgYygic2F0dXJhdGlvbmFsIiwgImNvbWJpQU5Ec2F0dXIiKSlbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiLCAiY29uZGl0aW9uIildKQpsZW5ndGgodW5pcXVlKHBsb3Rfc3Vic2V0JHNnUk5BX3RhcmdldCkpCmxlbmd0aCh1bmlxdWUocGxvdF9zdWJzZXQkc2dSTkFfdGFyZ2V0KSkvbGVuZ3RoKHVuaXF1ZShmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0KSkKCnAgPC0gZ2dwbG90KHBsb3Rfc3Vic2V0LCBhZXMoeD1ub3JtLCBncm91cD1jb25kaXRpb24sIGZpbGw9Y29uZGl0aW9uLCBjb2xvcj1jb25kaXRpb24sIGxpbmV0eXBlPWNvbmRpdGlvbikpICsgZ2VvbV9kZW5zaXR5KGFkanVzdD0xLjUsIGFscGhhPS40KSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIGxhYnModGl0bGU9IlNhdHVyYXRpb25hbCBDYmJNIGxpYnJhcnkiLCB4PSJOb3JtYWxpemVkIGZpdG5lc3MgdmFsdWUiKSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiQ0xfTjIiPSJzb2xpZCIsICJDTF9PMiI9ImRhc2hlZCIsICJMRCI9ImRvdHRlZCIpLCBsYWJlbHM9YygiQ29udGludW91cyBsaWdodCwgTjIgZmVlZCIsICJDb250aW51b3VzIGxpZ2h0LCBPMiBmZWVkIiwgIkxpZ2h0LWRhcmsgY3ljbGVzLCBPMiBmZWVkIikpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NhdHVyYXRpb25hbF9saWJyYXJ5X2RlbnNpdHlwbG90LnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTgpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zYXR1cmF0aW9uYWxfbGlicmFyeV9kZW5zaXR5cGxvdC5wbmciLCBwbG90PXAsIHdpZHRoPTExLjUsIGhlaWdodD04KQpgYGAKCmBgYHtyfQpmb3IoY29uZCBpbiB1bmlxdWUocGxvdF9zdWJzZXQkY29uZGl0aW9uKSl7CiAgcHJpbnQoY29uZCkKICB0ZW1wX3N1YnMgPC0gc3Vic2V0KHBsb3Rfc3Vic2V0LCBwbG90X3N1YnNldCRjb25kaXRpb249PWNvbmQpCiAgcHJpbnQobnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJBYm92ZSAxIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSkpKQogIHByaW50KCJBYm92ZSAxLCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSkpL25yb3codGVtcF9zdWJzKSkKICBwcmludCgiQWJvdmUgMSBhbmQgc2lnbmlmaWNhbnRseSBoaWdoZXIgdGhhbiBXVCIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA+IDEgJiB0ZW1wX3N1YnMkcF9maXRfYWRqX1dUIDwgMC4wNSkpKQogIHByaW50KCJBYm92ZSAxIGFuZCBzaWduaWZpY2FudGx5IGhpZ2hlciB0aGFuIFdULCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSAmIHRlbXBfc3VicyRwX2ZpdF9hZGpfV1QgPCAwLjA1KSkvbnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJCZWxvdyAwIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtIDwgMCkpKQogIHByaW50KCJCZWxvdyAwLCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtIDwgMCkpL25yb3codGVtcF9zdWJzKSkKfQpgYGAKCiMjIyBDb21wYXJpc29uIGNvbWJpbmF0b3JpYWwgYW5kIHNhdHVyYXRpb25hbCBsaWJyYXJ5CgpXaGVuIGNvbXBhcmluZyBmaXRuZXNzIHZhbHVlcyBiZXR3ZWVuIHRoZSBjb21iaW5hdG9yaWFsIGFuZCBzYXR1cmF0aW9uYWwgcGFydCBkaXJlY3RseSwgaXQgYmVjb21lcyBvYnZpb3VzIHRoYXQgdGhlIHNhdHVyYXRpb25hbCBwYXJ0IGNvbnRhaW5zLCByZWxhdGl2ZWx5IHNwZWFraW5nLCBtb3JlIHZhcmlhbnRzIHdpdGggZnVuY3Rpb25hbCBSdWJpc2NvIHZhcmlhbnRzLCBpLmUuIHRoZSByaWdodCBwZWFrIG9mIHRoZSBiaW1vZGFsIGRpc3RyaWJ1dGlvbiBpcyBoaWdoZXIgdGhhbiB0aGUgbGVmdCBwZWFrIGNvbnRyYXN0aW5nIHRvIHRoZSBjYXNlIGluIHRoZSBjb21iaW5hdG9yaWFsIHBhcnQgb2YgdGhlIGxpYnJhcnkuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KZml0bmVzc19kYXRhXzIgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJGNhdGVnb3J5IT0ibm90RXhwZWN0ZWQiICYgZml0bmVzc19kYXRhJGNhdGVnb3J5IT0iV1QiKQpmaXRuZXNzX2RhdGFfMltmaXRuZXNzX2RhdGFfMiRjYXRlZ29yeT09ImNvbWJpQU5Ec2F0dXIiLF0kY2F0ZWdvcnkgPC0gInNhdHVyYXRpb25hbCIKZml0bmVzc19kYXRhXzIgPC0gdW5pcXVlKGZpdG5lc3NfZGF0YV8yWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJwX2ZpdF9hZGpfV1QiLCAiY2F0ZWdvcnkiLCAiY29uZGl0aW9uIildKQpwIDwtIGdncGxvdChmaXRuZXNzX2RhdGFfMiwgYWVzKHg9bm9ybSwgZmlsbD1jYXRlZ29yeSwgY29sb3I9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5KSkgKyBnZW9tX2RlbnNpdHkoYWRqdXN0PTEuNSwgYWxwaGE9LjQpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKGNvbF9jb21iaV9zYXQpLCBsYWJlbHMgPSBjKCJDb21iaW5hdG9yaWFsIiwgIlNhdHVyYXRpb25hbCIpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yyhjb2xfY29tYmlfc2F0KSwgbGFiZWxzID0gYygiQ29tYmluYXRvcmlhbCIsICJTYXR1cmF0aW9uYWwiKSkgKyBsYWJzKHg9Ik5vcm1hbGl6ZWQgZml0bmVzcyB2YWx1ZSIpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0ic29saWQiLCAic2F0dXJhdGlvbmFsIj0iZGFzaGVkIiksIGxhYmVscz1jKCJDb21iaW5hdG9yaWFsIiwgIlNhdHVyYXRpb25hbCIpKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvZGVuc2l0eV9jb21wYXJlQ29tYmlTYXR1ci5wZGYiLCBwbG90PXAsIHdpZHRoPTEwLCBoZWlnaHQ9NCwgdW5pdHM9ImNtIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL2RlbnNpdHlfY29tcGFyZUNvbWJpU2F0dXIucG5nIiwgcGxvdD1wKQpgYGAKCiMjIE92ZXJ2aWV3IGdyb3d0aCBhbGwgdmFyaWFudHMKClRoZSBtZWFuIG9mIHRoZSBsb2cyRkMgb2YgZWFjaCB2YXJpYW50IHByZXNlbnQgaW4gdGhlIGxpYnJhcnkgaXMgc2hvd24uIFBpbmsgc2hvd3MgSzIxNEgsIHdoaWNoIGlzIHVuZGVyIGFsbCB0aHJlZSBjb25kaXRpb25zIHRoZSBLMjE0IHZhcmlhbnQgcGVyZm9ybWluZyBiZXN0LCBibGFjayB0aGUgV1QgdmFyaWFudC4gUHVycGxlIHNob3dzIEsyMTREIGFuZCBLMjE0SSwgdGhlIHR3byBLMjE0IHZhcmlhbnRzIHBlcmZvcm1pbmcgd29yc3QuIFRoZXJlIGlzIGEgY2xlYXIgc2VwYXJhdGlvbiBvZiBkaWZmZXJlbnQgdmFyaWFudHMsIFdUIGlzIGNsZWFybHkgZ2FpbmluZyBpbiByZWxhdGl2ZSBhYnVuZGFuY2UgYW5kIHRoZSBpbnZlc3RpZ2F0ZWQgSzIxNCB2YXJpYW50cyBhcmUgZHJvcHBpbmcuIFRoaXMgY29uZmlybXMgc3VjY2Vzc2Z1bCBzZWxlY3Rpb24gYWNjb3JkaW5nIHRvIFJ1YmlzY28gYWN0aXZpdHkuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpmaXRfZmFjdG9yIDwtIGZpdG5lc3NfZGF0YQpmaXRfZmFjdG9yJHNnUk5BX3RhcmdldCA8LSBmYWN0b3IoZml0X2ZhY3RvciRzZ1JOQV90YXJnZXQsIGxldmVscz1jKHVuaXF1ZShzdWJzZXQoZml0X2ZhY3RvciwgIWZpdF9mYWN0b3Ikc2dSTkFfdGFyZ2V0ICVpbiUgYygiV1QiLCAiSzIxNFIiKSkkc2dSTkFfdGFyZ2V0KSwgIldUIiwgIksyMTRSIikpCnAgPC0gZ2dwbG90KGZpdF9mYWN0b3IsIGFlcyh4PXRpbWUsIHk9d21lYW5fbG9nMkZvbGRDaGFuZ2UsIGdyb3VwPXNnUk5BX3RhcmdldCwgY29sb3I9c2dSTkFfdGFyZ2V0LCBsaW5ld2lkdGg9c2dSTkFfdGFyZ2V0LCBhbHBoYT1zZ1JOQV90YXJnZXQpKSArIGdlb21fbGluZSgpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJLMjE0UiI9InB1cnBsZSIsICJXVCI9ImJsYWNrIiksIG5hLnZhbHVlPSJsaWdodGdyYXkiKSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJUaW1lIChnZW5lcmF0aW9ucykiKSArIHlsYWIoImxvZzJGQyh0byBnPTApIikgKyBzY2FsZV9kaXNjcmV0ZV9tYW51YWwoImxpbmV3aWR0aCIsIHZhbHVlcz1jKCJLMjE0UiI9MC4zLCAiV1QiPTAuMyksIG5hLnZhbHVlPTAuMSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWMoIksyMTRSIj0xLCAiV1QiPTEpLCBuYS52YWx1ZT0wLjUpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9hbGxfc2dSTkF0YXJnZXRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXAsIGhlaWdodD0xLjUsIHdpZHRoPTUsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9hbGxfc2dSTkF0YXJnZXRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXAsIGhlaWdodD0xLjUsIHdpZHRoPTUsIHVuaXRzPSJpbiIpCmBgYAoKIyMgUGxvdCBXVCBhbmQgSzIxNCB2YXJpYW50cwoKYGBge3IgcGxvdHRpbmctZnVuY3Rpb25zLW1vcmVDb2xvci1DSSwgZWNobz1GQUxTRX0KeWxpbXMgPC0gYygtNi43LCA4LjApCgpsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyIDwtIGZ1bmN0aW9uKGRmX3RvX3Bsb3QpewogIHNlX3ZlY3RvciA8LSBkZl90b19wbG90JHNkX2xvZzJGb2xkQ2hhbmdlL3NxcnQoZGZfdG9fcGxvdCRudW1fYmFyY29kZXMpCiAgc2VfdmVjdG9yW2lzLm5hKHNlX3ZlY3RvcildIDwtIGRmX3RvX3Bsb3QkbGZjU0VbaXMubmEoc2VfdmVjdG9yKV0KICBkZl90b19wbG90JGNvbmYgPC0gcW5vcm0oMC45NzUpKnNlX3ZlY3RvcgogIHBsb3RfcCA8LSBnZ3Bsb3QoZGZfdG9fcGxvdCwgYWVzKHg9dGltZSwgeT13bWVhbl9sb2cyRm9sZENoYW5nZSwgeW1pbj13bWVhbl9sb2cyRm9sZENoYW5nZS1jb25mLCB5bWF4PXdtZWFuX2xvZzJGb2xkQ2hhbmdlK2NvbmYsIGdyb3VwPXNnUk5BX3RhcmdldCwgY29sb3I9ZmFjdG9yKHNnUk5BX3RhcmdldCksIGZpbGw9ZmFjdG9yKHNnUk5BX3RhcmdldCksIGxpbmV0eXBlPWZhY3RvcihzZ1JOQV90YXJnZXQpKSkgKyBuZXdfc2NhbGUoImFscGhhIikgKyBnZW9tX2xpbmUobGluZXdpZHRoPTAuMykgKyBnZW9tX3JpYmJvbihsaW5ldHlwZT0wLCBhbHBoYT0wLjI1KSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJUaW1lIChnZW5lcmF0aW9ucykiKSArIHlsYWIoIkxvZzJGQyIpICsgY29vcmRfY2FydGVzaWFuKHlsaW09eWxpbXMpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcmV0dXJuKHBsb3RfcCkKfQoKbGluZXBsb3Rfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIgPC0gZnVuY3Rpb24oZGZfdG9fcGxvdCl7CiAgcGxvdF9wIDwtIGdncGxvdChkZl90b19wbG90LCBhZXMoeD10aW1lLCB5PXdtZWFuX2xvZzJGb2xkQ2hhbmdlLCBjb2xvcj1mYWN0b3Ioc2dSTkFfdGFyZ2V0KSkpICsgbmV3X3NjYWxlKCJhbHBoYSIpICsgZ2VvbV9saW5lKGxpbmV3aWR0aD0wLjMpICsgdGhlbWVfbGlnaHQoKSArIHhsYWIoIlRpbWUgKGdlbmVyYXRpb25zKSIpICsgeWxhYigiTG9nMkZDIikgKyBmYWNldF93cmFwKH5jb25kaXRpb24pICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgY29vcmRfY2FydGVzaWFuKHlsaW09eWxpbXMpCiAgcmV0dXJuKHBsb3RfcCkKfQoKbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19sb2cyIDwtIGZ1bmN0aW9uKGRmX3RvX3Bsb3QpewogIGRmX3RvX3Bsb3QkY29uZiA8LSBxbm9ybSgwLjk3NSkqZGZfdG9fcGxvdCRsZmNTRQogIGFscGhhX3ZhbHVlcyA8LSAxLWRmX3RvX3Bsb3Qkd2VpZ2h0X2xmY1NFCiAgbmFtZXMoYWxwaGFfdmFsdWVzKSA8LSBkZl90b19wbG90JHNnUk5BCiAgcGxvdF9wIDwtIGdncGxvdChkZl90b19wbG90LCBhZXMoeD10aW1lLCB5PWxvZzJGb2xkQ2hhbmdlLCB5bWluPWxvZzJGb2xkQ2hhbmdlLWNvbmYsIHltYXg9bG9nMkZvbGRDaGFuZ2UrY29uZiwgZ3JvdXA9c2dSTkEsIGNvbG9yPWZhY3RvcihzZ1JOQSksIGZpbGw9ZmFjdG9yKHNnUk5BKSkpICsgbmV3X3NjYWxlKCJhbHBoYSIpICsgZ2VvbV9saW5lKGxpbmV3aWR0aD0wLjMpICsgZ2VvbV9yaWJib24obGluZXR5cGU9MCwgYWVzKGFscGhhPXNnUk5BKSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWFscGhhX3ZhbHVlcykgKyB0aGVtZV9saWdodCgpICsgeGxhYigiVGltZSAoZ2VuZXJhdGlvbnMpIikgKyB5bGFiKCJMb2cyRkMiKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgY29vcmRfY2FydGVzaWFuKHlsaW09eWxpbXMpCiAgcmV0dXJuKHBsb3RfcCkKfQoKbGluZXBsb3Rfc2V2ZXJhbENvbG91cnNfbG9nMiA8LSBmdW5jdGlvbihkZl90b19wbG90KXsKICBwbG90X3AgPC0gZ2dwbG90KGRmX3RvX3Bsb3QsIGFlcyh4PXRpbWUsIHk9bG9nMkZvbGRDaGFuZ2UsIGdyb3VwPXNnUk5BLCBjb2xvcj1mYWN0b3Ioc2dSTkEpKSkgKyBuZXdfc2NhbGUoImFscGhhIikgKyBnZW9tX2xpbmUobGluZXdpZHRoPTAuMykgKyB0aGVtZV9saWdodCgpICsgeGxhYigiVGltZSAoZ2VuZXJhdGlvbnMpIikgKyB5bGFiKCJMb2cyRkMiKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT15bGltcykKICByZXR1cm4ocGxvdF9wKQp9CmBgYAoKVGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIGZvciB0aGUgd2VpZ2h0ZWQgbWVhbiBsb2cyRkMgb2YgYWxsIFdUIGJhcmNvZGVzIGlzIHJlbGF0aXZlbHkgc21hbGwsIG1lYW5pbmcgdGhhdCB3ZSBjYW4gcmVseSByZWxhdGl2ZWx5IHdlbGwgb24gdGhlIHJlc3BlY3RpdmUgZGF0YS4gSW4gdG90YWwsIDMwIGRpZmZlcmVudCBiYXJjb2RlcyBmb3IgV1Qgd2VyZSB0YWtlbiBpbnRvIGFjY291bnQuIFRoZXNlIHdlcmUgdGhlIGhpZ2hlc3QgMzAgYmFyY29kZXMgYXNzaWduZWQgdG8gdGhlIFdUIHZhcmlhbnQgcHJlc2VudCBpbiB0aGUgY29tcGxldGUgZGF0YSBzZXQuIFRoZSBoaWdoIG51bWJlciBvZiBXVCBiYXJjb2RlcyBhcyB3ZWxsIGFzIHRoZWlyIGhpZ2ggY291bnQgbnVtYmVycyBib3RoIGNvbnRyaWJ1dGUgdG8gYSByZWxpYWJsZSBlc3RpbWF0ZSBvZiB0aGUgY29ycmVzcG9uZGluZyBmaXRuZXNzLgoKYGBge3IgIG1lc3NhZ2U9RkFMU0V9CldUX3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldD09IldUIilbLGMoIm51bV9iYXJjb2RlcyIsICJzZF9sb2cyRm9sZENoYW5nZSIsICJsZmNTRSIsICJ0aW1lIiwgIndtZWFuX2xvZzJGb2xkQ2hhbmdlIiwgInNnUk5BX3RhcmdldCIsICJzZ1JOQSIsICJsb2cyRm9sZENoYW5nZSIsICJ3ZWlnaHRfbGZjU0UiLCAiY29uZGl0aW9uIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoV1Rfc3Vic2V0KSArIGxhYnModGl0bGU9IldUIHdpdGggOTUlIENJIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvV1RfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL1dUX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCmBgYAoKSGVyZSwgdGhlIGxvZzJGQyBjaGFydHMgb2YgdGhlIGRpZmZlcmVudCBiYXJjb2RlcyB1c2VkIGZvciBkZXRlcm1pbmluZyBXVCBkYXRhIGFyZSBwbG90dGVkLiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIGNvbG9yZWQgYWNjb3JkaW5nIHRvIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbG9nMkZDIC0gdGhlIG1vcmUgdHJhbnNwYXJlbnQsIHRoZSBoaWdoZXIgdGhlIHN0YW5kYXJkIGVycm9yIGFuZCB0aGUgb3RoZXIgd2F5IHJvdW5kLiBUaGUgYmFyY29kZXMgZGV2aWF0aW5nIGZyb20gdGhlIG92ZXJhbGwgc3RhbmRhcmQgaGF2ZSBoaWdoZXIgc3RhbmRhcmQgZXJyb3JzIHRoYW4gdGhlIGJhcmNvZGVzIHdoaWNoIGFyZSBjbG9zZSB0byB0aGUgb3ZlcmFsbCBtZWFuLiBJbiBibGFjaywgdGhlIG92ZXJhbGwgdHJlbmQgaXMgYWRkZWQuCgpJdCBzZWVtcyBhcyBpZiBtb3N0IG9mIHRoZSAzMCBiYXJjb2RlcyBhZ3JlZWQgd2VsbC4gTm90ZSB0aGF0LCBmb3IgY29udGludW91cyBsaWdodCBhbmQgYW4gTzItY29udGFpbmluZyBmZWVkLCBzZXZlcmFsIGJhcmNvZGVzIGdvIGEgYml0IGRvd24gZm9yIHRoZSBsYXN0IHRpbWUgcG9pbnQuIEl0IHNlZW1zIGFzIGlmIHNlbGVjdGlvbiB3YXMgYWxyZWFkeSBsb3N0IHRoZW4gb3IgYXQgbGVhc3QgZ290IG11Y2ggd2Vha2VyLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19sb2cyKFdUX3N1YnNldCkgKyBsYWJzKHRpdGxlPSJXVCBiYXJjb2RlcyB3aXRoIDk1JSBDSSIpICsgZ2VvbV9saW5lKGFlcyh4PXRpbWUsIHk9d21lYW5fbG9nMkZvbGRDaGFuZ2UsIGdyb3VwPXNnUk5BX3RhcmdldCksIGNvbG9yPSJibGFjayIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL1dUX2JhcmNvZGVzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9XVF9iYXJjb2Rlc190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKbGluZXBsb3Rfc2V2ZXJhbENvbG91cnNfbG9nMihXVF9zdWJzZXQpICsgbGFicyh0aXRsZT0iV1QgYmFyY29kZXMiKSArIGdlb21fbGluZShhZXMoeD10aW1lLCB5PXdtZWFuX2xvZzJGb2xkQ2hhbmdlLCBncm91cD1zZ1JOQV90YXJnZXQpLCBjb2xvcj0iYmxhY2siKQpgYGAKCkhlcmUsIGFsbCBLMjE0IG11dGFudCB2YXJpYW50cyBhbmQgdGhlaXIgd2VpZ2h0ZWQgbWVhbiBsb2cyRm9sZENoYW5nZSBhcmUgcGxvdHRlZC4gQWxsIGhhdmUgYSB0ZW5kZW5jeSB0byBkZWNyZWFzZSBpbiByZWxhdGl2ZSBhYnVuZGFuY2UuIEZvciBjb250aW51b3VzIGxpZ2h0IGFuZCBhbiBPMi1jb250YWluaW5nIGdhcyBmZWVkLCB0aGUgbGFzdCB0aW1lIHBvaW50IGxvb2tzIGFzIGlmIHNlbGVjdGlvbiBnb3Qgd29yc2UuIFNhbWUgYWxzbyBob2xkcywgdG8gc29tZSBkZWdyZWUsIGZvciB0aGUgTjItY29udGFpbmluZyBmZWVkLiBLMjE0SCBnb2VzIHByZXR0eSB3aWxkIGluIHRoZSBMRCBjb25kaXRpb24uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KSzIxNF9zdWJzZXQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGdyZXBsKCJLMjE0IiwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCkpWyxjKCJudW1fYmFyY29kZXMiLCAic2RfbG9nMkZvbGRDaGFuZ2UiLCAibGZjU0UiLCAidGltZSIsICJ3bWVhbl9sb2cyRm9sZENoYW5nZSIsICJzZ1JOQV90YXJnZXQiLCAic2dSTkEiLCAibG9nMkZvbGRDaGFuZ2UiLCAid2VpZ2h0X2xmY1NFIiwgImNvbmRpdGlvbiIsICJub3JtIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoSzIxNF9zdWJzZXQpICsgbGFicyh0aXRsZT0iSzIxNCB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL0syMTRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL0syMTRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCmxpbmVwbG90X3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKEsyMTRfc3Vic2V0KSArIGxhYnModGl0bGU9IksyMTQgdmFyaWFudHMiKQpgYGAKCksyMTRIIGlzIHRoZSBLMjE0IHZhcmlhbnQgd2l0aCB0aGUgaGlnaGVzdCBmaXRuZXNzIHNjb3JlIHVuZGVyIGFsbCB0aHJlZSBpbnZlc3RpZ2F0ZWQgY29uZGl0aW9ucy4gSXRzIGZpdG5lc3MgdmFsdWUgd2FzIGRldGVybWluZWQgb24gYmFzaXMgb2YgMyBiYXJjb2Rlcywgb2Ygd2hpY2ggb25lIHNob3dzIGEgc2xvd2VyIGRlY3JlYXNlIHRoYW4gdGhlIG90aGVyIDIgKGluIENMX08yIGFuZCBMRCwgaXQgZG9lcyBub3QgZXZlbiBzaG93IGEgZGVjcmVhc2UpLCBidXQgaGFzIGEgbG93ZXIgc3RhbmRhcmQgZXJyb3IuIEluIGJsYWNrLCB0aGUgd2VpZ2h0ZWQgbWVhbiBsb2cyRkMgaXMgc2hvd24uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KSzIxNEhfc3Vic2V0IDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBncmVwbCgiSzIxNEgiLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0KSlbLGMoIm51bV9iYXJjb2RlcyIsICJzZF9sb2cyRm9sZENoYW5nZSIsICJsZmNTRSIsICJ0aW1lIiwgIndtZWFuX2xvZzJGb2xkQ2hhbmdlIiwgInNnUk5BX3RhcmdldCIsICJzZ1JOQSIsICJsb2cyRm9sZENoYW5nZSIsICJ3ZWlnaHRfbGZjU0UiLCAiY29uZGl0aW9uIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbG9nMihLMjE0SF9zdWJzZXQpICsgbGFicyh0aXRsZT0iSzIxNEggYmFyY29kZXMgd2l0aCA5NSUgQ0kiKSArIGdlb21fbGluZShhZXMoeD10aW1lLCB5PXdtZWFuX2xvZzJGb2xkQ2hhbmdlLCBncm91cD1zZ1JOQV90YXJnZXQpLCBjb2xvcj0iYmxhY2siKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9LMjE0SF9iYXJjb2Rlc190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvSzIxNEhfYmFyY29kZXNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKYGBgCgpDb21wYXJlIEsyMTRIIHRvIFdULiBJbiBMRCwgZHVlIHRvIHRoZSBzdHJvbmdseSByaXNpbmcgYmFyY29kZSwgSzIxNEggYW5kIFdUIGFyZSBub3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgKGlmIHdlIHRha2UgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGFzIG1lYXN1cmUgZm9yICJDb3VsZCBpdCBldmVuIGJlIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSIpLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihiaW5kX3Jvd3MoV1Rfc3Vic2V0LEsyMTRIX3N1YnNldCkpICsgbGFicyh0aXRsZT0iSzIxNEggYW5kIFdUIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvV1RfSzIxNEhfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL1dUX0syMTRIX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCmBgYAoKSzIxNCB2YXJpYW50cyBLMjE0RCAoTEQpLCBLMjE0UiAoQ0xfTjIpIGFuZCBLMjE0RSAoQ0xfTzIpIHdlcmUgdGhlIG9uZXMgd2hpY2ggd2VyZSB1c2VkIGZvciBtZWRpYW4gbm9ybWFsaXphdGlvbiAoY29tcGFyZSBjb21wYXJlX3dlaWdodGluZ1N0cmF0ZWdpZXNfMm5kUm91bmQuaHRtbCkuIEFsbCBsb29rIGFzIGlmIHRoZXkgd2VyZSBnb29kIG5lZ2F0aXZlIGNvbnRyb2xzIGluIHN1YnNlcXVlbnQgcGxvdHMuIEsyMTRSIGlzIGNsb3Nlc3QgdG8gYSBub3JtYWxpemVkIGZpdG5lc3MgdmFsdWUgb2YgMCBvZiB0aGVzZSB0aHJlZS4gV2hlbiBjYWxjdWxhdGluZyB0aGUgYWJzb2x1dGUgZGlzdGFuY2UgdG8gMCBvZiBhbGwgdmFyaWFudHMgaW4gdGhlIG5vcm1hbGl6ZWQgZGF0YSBzZXQsIEsyMTRMIGFsc28gc2VlbXMgdG8gYmUgYSBnb29kIGNhbmRpZGF0ZS4gU2luY2UgSzIxNFIgaGFzIGEgbG93ZXIgR2luaSBpbmRleCBhbmQgYSBjb21wYXJhYmxlIGFic29sdXRlIGRpc3RhbmNlIHRvIDAgaW4gYWxsIGRhdGEgc2V0cywgSzIxNFIgd2lsbCBiZSBwbG90dGVkIGFzIG5lZ2F0aXZlIGNvbnRyb2wgc3Vic2VxdWVudGx5LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CksyMTREUlRfc3Vic2V0IDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzIxNEQiLCAiSzIxNFIiLCAiSzIxNEUiLCAiSzIxNEwiKSlbLGMoIm51bV9iYXJjb2RlcyIsICJzZF9sb2cyRm9sZENoYW5nZSIsICJsZmNTRSIsICJ0aW1lIiwgIndtZWFuX2xvZzJGb2xkQ2hhbmdlIiwgInNnUk5BX3RhcmdldCIsICJzZ1JOQSIsICJsb2cyRm9sZENoYW5nZSIsICJ3ZWlnaHRfbGZjU0UiLCAiY29uZGl0aW9uIiwgIm5vcm0iKV0pCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihiaW5kX3Jvd3MoV1Rfc3Vic2V0LEsyMTREUlRfc3Vic2V0KSkgKyBsYWJzKHRpdGxlPSJLMjE0IHZhcmlhbnRzIGFuZCBXVCIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL1dUX0syMTREUlRMX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9XVF9LMjE0RFJUTF90aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKcGl2b3Rfd2lkZXIodW5pcXVlKEsyMTREUlRfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAiY29uZGl0aW9uIiwgIm5vcm0iKV0pLCB2YWx1ZXNfZnJvbT1ub3JtLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKCksyMTRfc2V0IDwtIHBpdm90X3dpZGVyKHVuaXF1ZShLMjE0X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgImNvbmRpdGlvbiIsICJub3JtIildKSwgdmFsdWVzX2Zyb209bm9ybSwgbmFtZXNfZnJvbT1jb25kaXRpb24pCksyMTRfc2V0JHN1bSA8LSBhcHBseShhYnMoSzIxNF9zZXRbLGMoMjo0KV0pLCAxLCBzdW0pCksyMTRfc2V0JGdpbmkgPC0gYXBwbHkoYWJzKEsyMTRfc2V0WyxjKDI6NCldKSwgMSwgR2luaSkKSzIxNF9zZXRbb3JkZXIoSzIxNF9zZXQkc3VtKSxdCgpLMjE0TF9zdWJzZXQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJLMjE0TCIsICJLMjE0UiIpKVssYygibnVtX2JhcmNvZGVzIiwgInNkX2xvZzJGb2xkQ2hhbmdlIiwgImxmY1NFIiwgInRpbWUiLCAid21lYW5fbG9nMkZvbGRDaGFuZ2UiLCAic2dSTkFfdGFyZ2V0IiwgInNnUk5BIiwgImxvZzJGb2xkQ2hhbmdlIiwgIndlaWdodF9sZmNTRSIsICJjb25kaXRpb24iLCAibm9ybSIpXSkKbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihiaW5kX3Jvd3MoV1Rfc3Vic2V0LEsyMTRMX3N1YnNldCkpICsgbGFicyh0aXRsZT0iSzIxNEwsIEsyMTRSIGFuZCBXVCIpCm5lZ19jb250cm9sX0syMTQgPC0gIksyMTRSIgpgYGAKCkFsbCBLMjE0IHZhcmlhbnRzIGFuZCBXVAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnAgPC0gbGluZXBsb3Rfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoYmluZF9yb3dzKEsyMTRfc3Vic2V0LCBXVF9zdWJzZXQpKSArIGxhYnModGl0bGU9IksyMTQgdmFyaWFudHMgYW5kIFdUIikgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIldUIj0iYmxhY2siKSwgbmEudmFsdWU9ImxpZ2h0Z3JheSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL1dUX0syMTRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL1dUX0syMTRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKYGBgCgojIyBCYXJwbG90cyBudW1iZXIgbXV0YXRpb25zIH4gZml0bmVzcwoKV2hlbiBpbnZlc3RpZ2F0aW5nIGFsbCBtdXRhbnQgdmFyaWFudHMgcHJlc2VudCBpbiB0aGUgbGlicmFyeSwgaW5jbHVkaW5nIHRoZSBvbmVzIHdoaWNoIG9jY3VycmVkIHNwb250YW5lb3VzbHksIHRoZXJlIGlzIGEgY2xlYXIgdHJlbmQgb2JzZXJ2YWJsZSB0aGF0IHRoZSBtb3JlIGFtaW5vIGFjaWQgZXhjaGFuZ2VzL211dGF0aW9ucyB3ZXJlIGludHJvZHVjZWQsIHRoZSBsb3dlciB0aGUgbWVhbiBmaXRuZXNzLiBUaGlzIG1hdGNoZXMgd2VsbCB3aXRoIHdoYXQgd2FzIHJlcG9ydGVkIGluIGxpdGVyYXR1cmUgYmVmb3JlLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnJlZF9maXRuZXNzIDwtIHVuaXF1ZShmaXRuZXNzX2RhdGFbYygic2dSTkFfdGFyZ2V0IiwgIm1lYW5fZml0bmVzcyIsICJjb25kaXRpb24iLCAiY2F0ZWdvcnkiLCAibnVtYmVyX211dHMiLCAibm9ybSIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIildKQpwIDwtIGdncGxvdChyZWRfZml0bmVzcywgYWVzKHg9bnVtYmVyX211dHMsIHk9bm9ybSwgZ3JvdXA9bnVtYmVyX211dHMsIGNvbG91cj1udW1iZXJfbXV0cywgZmlsbD1udW1iZXJfbXV0cykpICsgZ2VvbV9wb2ludChzaXplPTAuMSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGg9MC45KSwgY29sb3I9IiM0YTRhNGFmZiIpICsgdGhlbWVfbGlnaHQoKSArIHN0YXRfc3VtbWFyeShmdW49Im1lYW4iLCBnZW9tPSJiYXIiLCBhbHBoYT0wLjMsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSksIGxpbmV3aWR0aD0wLCBjb2xvcj0iIzRhNGE0YWZmIiwgZmlsbD0iIzRhNGE0YWZmIikgKyB5bGFiKCJXZWlnaHRlZCBtZWFuIGZpdG5lc3MiKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikrIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvYmFyX2ZpdG5lc3NfZGF0YV9udW1iZXJNdXRzX2NvbXBsZXRlLnBkZiIsIHAsIHdpZHRoPTMwLCBoZWlnaHQ9MTgsIHVuaXRzPSJjbSIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9iYXJfZml0bmVzc19kYXRhX251bWJlck11dHNfY29tcGxldGUucG5nIiwgcCwgd2lkdGg9MzAsIGhlaWdodD0xOCwgdW5pdHM9ImNtIikKYGBgCgpXaGVuIGNoZWNraW5nIHRoZSB3aG9sZSAiZXhwZWN0ZWQiIGxpYnJhcnksIGkuZS4gdGhlIGNvbWJpbmF0aW9uIG9mIHNhdHVyYXRpb25hbCBhbmQgY29tYmluYXRvcmlhbCBsaWJyYXJ5LCB0aGlzIHRyZW5kIGlzIHN0aWxsIG9ic2VydmFibGUuCgpgYGB7ciBiYXJfZml0bmVzc19kYXRhX04yLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KZXhwZWN0ZWRfcmVkX2ZpdG5lc3MgPC0gc3Vic2V0KHJlZF9maXRuZXNzLCAhcmVkX2ZpdG5lc3MkY2F0ZWdvcnk9PSJub3RFeHBlY3RlZCIpCnAgPC0gZ2dwbG90KGV4cGVjdGVkX3JlZF9maXRuZXNzLCBhZXMoeD1udW1iZXJfbXV0cywgeT1ub3JtLCBmaWxsPW51bWJlcl9tdXRzLCBjb2xvdXI9bnVtYmVyX211dHMsIGdyb3VwPW51bWJlcl9tdXRzKSkgKyBzdGF0X3N1bW1hcnkoZnVuPSJtZWFuIiwgZ2VvbT0iYmFyIiwgYWxwaGE9MC4zLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjkpLCBsaW5ld2lkdGg9MCwgY29sb3I9IiM0YTRhNGFmZiIsIGZpbGw9IiM0YTRhNGFmZiIpICsgZ2VvbV9wb2ludChzaXplPTAuMDUsIGFscGhhPTAuNywgcG9zaXRpb249cG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGg9MC45KSwgY29sb3I9IiM0YTRhNGFmZiIpICsgdGhlbWVfbGlnaHQoKSArIHlsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyBzY29yZSIpICsgeGxhYigiTnVtYmVyIG9mIGludG9kdWNlZCBhbWlubyBhY2lkIGV4Y2hhbmdlcyIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBmYWNldF93cmFwKH5jb25kaXRpb24pICsgY29vcmRfY2FydGVzaWFuKHhsaW09YygtMC4yLDcuMikpKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2Jhcl9maXRuZXNzX2RhdGFfbnVtYmVyTXV0c19vbmx5RXhwZWN0ZWQucGRmIiwgcCwgd2lkdGg9NSwgaGVpZ2h0PTIuNCwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL2Jhcl9maXRuZXNzX2RhdGFfbnVtYmVyTXV0c19vbmx5RXhwZWN0ZWQucG5nIiwgcCwgd2lkdGg9NSwgaGVpZ2h0PTMsIHVuaXRzPSJpbiIpCmBgYAoKV2hlbiBvbmx5IGNvbXBhcmluZyB0aGUgYW1pbm8gYWNpZCBleGNoYW5nZXMgcHJlc2VudCBpbiB0aGUgY29tYmluYXRvcmlhbCBsaWJyYXJ5LCB0aGUgb3ZlcmFsbCB0cmVuZCB0aGF0IGludHJvZHVjaW5nIGFub3RoZXIgYW1pbm8gYWNpZCBleGNoYW5nZSBkZWNyZWFzZXMgdGhlIGZpdG5lc3MgdmFsdWUgYmVjb21lcyBldmVuIGNsZWFyZXIuIEF0IHRoZSBzYW1lIHRpbWUsIGFkZGl0aW9uYWxseSBpbnRyb2R1Y2VkIGV4Y2hhbmdlcyBkbyBub3QgcmVkdWNlIHRoZSBtYXhpbXVtIGZpdG5lc3MgdmFsdWUuCgpgYGB7ciBiYXJfZml0bmVzc19kYXRhX29ubHlDb21iaSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CmNvbWJpbmF0b3JpYWxfd2lkZSA8LSBzdWJzZXQoZXhwZWN0ZWRfcmVkX2ZpdG5lc3MsICFleHBlY3RlZF9yZWRfZml0bmVzcyRjYXRlZ29yeT09InNhdHVyYXRpb25hbCIpCmxtX2ZpdG5lc3NfZGF0YSA8LSBsbShub3JtIH4gbnVtYmVyX211dHMsIGNvbWJpbmF0b3JpYWxfd2lkZSkKc3VtbWFyeShsbV9maXRuZXNzX2RhdGEpCgpsbV9maXRuZXNzX2RhdGEgPC0gbG0obm9ybSB+IG51bWJlcl9tdXRzLCBzdWJzZXQoY29tYmluYXRvcmlhbF93aWRlLCBjb21iaW5hdG9yaWFsX3dpZGUkY29uZGl0aW9uID09ICJDTF9OMiIpKQpzdW1tYXJ5KGxtX2ZpdG5lc3NfZGF0YSkKCmxtX2ZpdG5lc3NfZGF0YSA8LSBsbShub3JtIH4gbnVtYmVyX211dHMsIHN1YnNldChjb21iaW5hdG9yaWFsX3dpZGUsIGNvbWJpbmF0b3JpYWxfd2lkZSRjb25kaXRpb24gPT0gIkNMX08yIikpCnN1bW1hcnkobG1fZml0bmVzc19kYXRhKQoKbG1fZml0bmVzc19kYXRhIDwtIGxtKG5vcm0gfiBudW1iZXJfbXV0cywgc3Vic2V0KGNvbWJpbmF0b3JpYWxfd2lkZSwgY29tYmluYXRvcmlhbF93aWRlJGNvbmRpdGlvbiA9PSAiTEQiKSkKc3VtbWFyeShsbV9maXRuZXNzX2RhdGEpCgpwIDwtIGdncGxvdChjb21iaW5hdG9yaWFsX3dpZGUsIGFlcyh4PW51bWJlcl9tdXRzLCB5PW5vcm0sIGZpbGw9bnVtYmVyX211dHMsIGNvbG91cj1udW1iZXJfbXV0cykpICsgc3RhdF9zdW1tYXJ5KGZ1bj0ibWVhbiIsIGdlb209ImJhciIsIGFscGhhPTAuMywgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC45KSwgbGluZXdpZHRoPTAsIGNvbG9yPSIjNGE0YTRhZmYiLCBmaWxsPSIjNGE0YTRhZmYiKSArIGdlb21fcG9pbnQoc2l6ZT0wLjEsIHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcmRvZGdlKGRvZGdlLndpZHRoPTAuOSksIGNvbG9yPSIjNGE0YTRhZmYiKSArIHRoZW1lX2xpZ2h0KCkgKyB5bGFiKCJOb3JtYWxpemVkIGZpdG5lc3Mgc2NvcmUiKSsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKC0wLjIsNy4yKSkKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvYmFyX2ZpdG5lc3NfZGF0YV9udW1iZXJNdXRzX29ubHlDb21iaS5wZGYiLCBwLCB3aWR0aD0zMCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvYmFyX2ZpdG5lc3NfZGF0YV9udW1iZXJNdXRzX29ubHlDb21iaS5wbmciLCBwLCB3aWR0aD0zMCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQpgYGAKCldoZW4gb25seSBjaGVja2luZyB2YXJpYW50cyB3aXRoIGZpdG5lc3MgdmFsdWVzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IHRvIHRoZSAid2lsZC10eXBlIiBzZXF1ZW5jZSwgdGhpcyBwYXR0ZXJuIGdldHMgZGFtcGVuZWQsIGV2ZW4gdGhvdWdoIHRoZXJlIGlzIHN0aWxsIHNvbWUgdHJlbmQgaW4gdGhpcyBkaXJlY3Rpb24gb2JzZXJ2YWJsZSB3aXRoIG1vcmUgYW5kIG1vcmUgdmFyaWFudHMgc2hvd2luZyByZWxhdGl2ZWx5IGhpZ2ggc2NvcmVzLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CmNvbWJpbmF0b3JpYWxfd2lkZSA8LSBzdWJzZXQoY29tYmluYXRvcmlhbF93aWRlLCBjb21iaW5hdG9yaWFsX3dpZGUkcF9maXRfYWRqX1dUPDAuMDUpCgpwIDwtIGdncGxvdChjb21iaW5hdG9yaWFsX3dpZGUsIGFlcyh4PW51bWJlcl9tdXRzLCB5PW5vcm0sIGZpbGw9bnVtYmVyX211dHMsIGNvbG91cj1udW1iZXJfbXV0cykpICsgZ2VvbV9wb2ludChzaXplPTAuMSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGg9MC45KSkgKyB0aGVtZV9saWdodCgpICsgc3RhdF9zdW1tYXJ5KGZ1bj0ibWVhbiIsIGdlb209ImJhciIsIGFscGhhPTAuMywgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC45KSwgbGluZXdpZHRoPTApICsgeWxhYigiV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIikgKyBmYWNldF93cmFwKH5jb25kaXRpb24pICsgY29vcmRfY2FydGVzaWFuKHhsaW09YygwLjgsNy4yKSkrIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9iYXJfZml0bmVzc19kYXRhX251bWJlck11dHNfb25seUNvbWJpX29ubHlTaWduaWZpY2FudC5wZGYiLCBwLCB3aWR0aD0xOCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvYmFyX2ZpdG5lc3NfZGF0YV9udW1iZXJNdXRzX29ubHlDb21iaV9vbmx5U2lnbmlmaWNhbnQucG5nIiwgcCwgd2lkdGg9MTgsIGhlaWdodD0xOCwgdW5pdHM9ImNtIikKYGBgCgojIyBDb3JyZWxhdGlvbiB3aXRoIGRpZmZlcmVudCB6ZXJvLXNob3QgcHJlZGljdG9ycyBhbmQgc2ltaWxhcgoKVGhlcmUgYXJlIGRpZmZlcmVudCB3YXlzIG9mIHByZWRpY3RpbmcgdGhlIGVmZmVjdCBvZiBoaWdoZXIgb3JkZXIgbXV0YW50IHZhcmlhbnRzLiBGb3IgZGVzaWduaW5nIHRoZSBsaWJyYXJ5LCBubyBwcmlvciBrbm93bGVkZ2Ugd2FzIGF2YWlsYWJsZS4gRm9yIHRoaXMgcmVhc29uLCB6ZXJvLXNob3QgcHJlZGljdGlvbnMgdXNpbmcgRVZtdXRhdGlvbiBhbmQgRGVlcFNlcXVlbmNlIHdlcmUgcGVyZm9ybWVkLiBXaGVuIGRhdGEgZm9yIHNpbmdsZS1zaXRlIG11dGFudHMgaXMgYXZhaWxhYmxlLCBhbiBhZGRpdGl2ZSBtb2RlbCBjYW4gYmUgdXNlZCB0aGF0IGlzIGFzc3VtaW5nIGluZGVwZW5kZW5jZSBvZiBkaWZmZXJlbnQgYW1pbm8gYWNpZCBleGNoYW5nZXMuIFByZWRpY3Rpb25zIG9mIHRoaXMgbW9kZWwgd2VyZSBjYWxjdWxhdGVkIHVzaW5nIHB5dGhvbi4KCmBgYHtyfQpjb21iaV9zYXRfYWxwaGEgPC0gYyhjb21iaW5hdG9yaWFsPTAuMiwgc2F0dXJhdGlvbmFsPTAuMiwgV1Q9MS4wKQpjb21iaV9zYXRfc2hhcGUgPC0gYyhjb21iaW5hdG9yaWFsPTE2LCBzYXR1cmF0aW9uYWw9MTYsIFdUPTQpCmNvbWJpX3NhdF9zaXplIDwtIGMoY29tYmluYXRvcmlhbD0wLjQsIHNhdHVyYXRpb25hbD0wLjQsIFdUPTAuOCkKZG90c2l6ZSA8LSAwLjAyCnBvaW50c2hhcGUgPC0gMTYKYWxwaGFsZXZlbCA8LSAwLjQKbGluZV93aWR0aF9mb3JfcGxvdHMgPC0gMC40CnJlZF9maXRuZXNzIDwtIHVuaXF1ZShmaXRuZXNzX2RhdGFbYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgImNhdGVnb3J5IiwgIkVWY291cF9wcmVkaWN0IiwgIkRlZXBTZXFfcHJlZGljdCIsICJNU0FfVHJhbnNmb3JtIiwgImFkZGl0aXZlX3Njb3JlIiwgInByb3RlaW5OUFRfcHJlZGljdCIpXSkKcmVkX2ZpdG5lc3NbcmVkX2ZpdG5lc3MkY2F0ZWdvcnk9PSJjb21iaUFORHNhdHVyIixdJGNhdGVnb3J5IDwtICJzYXR1cmF0aW9uYWwiCnJlZF9maXRuZXNzIDwtIHN1YnNldChyZWRfZml0bmVzcywgcmVkX2ZpdG5lc3MkY2F0ZWdvcnkgIT0gIm5vdEV4cGVjdGVkIikKcmVkX2ZpdG5lc3NbcmVkX2ZpdG5lc3MkY2F0ZWdvcnk9PSJXVCIsXSRFVmNvdXBfcHJlZGljdCA8LSAwCmBgYAoKIyMjIENvbXBhcmlzb24gRVZtdXRhdGlvbiwgRGVlcFNlcXVlbmNlCgpgYGB7cn0KcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxIDwtIHN1YnNldChyZWRfZml0bmVzcywgIWlzLm5hKHJlZF9maXRuZXNzJERlZXBTZXFfcHJlZGljdCkpCgpmb3IoY2F0IGluIGMoImNvbWJpbmF0b3JpYWwiLCAic2F0dXJhdGlvbmFsIikpewogIHByaW50KGNhdCkKICBzdWJzZXRfZm9yTG9vcCA8LSBzdWJzZXQocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLCByZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY2F0ZWdvcnk9PWNhdCAmIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjb25kaXRpb249PSJDTF9OMiIpCiAgbG0gPC0gbG0oRGVlcFNlcV9wcmVkaWN0fkVWY291cF9wcmVkaWN0LCBzdWJzZXRfZm9yTG9vcCkKICBjb3JyZWxhdGlvbiA8LSBjb3IudGVzdChzdWJzZXRfZm9yTG9vcCRFVmNvdXBfcHJlZGljdCwgc3Vic2V0X2Zvckxvb3AkRGVlcFNlcV9wcmVkaWN0LCBtZXRob2QgPSAnc3BlYXJtYW4nKQogIHByaW50KGNvcnJlbGF0aW9uKQogIGNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHN1YnNldF9mb3JMb29wJEVWY291cF9wcmVkaWN0LCBzdWJzZXRfZm9yTG9vcCREZWVwU2VxX3ByZWRpY3QsIG1ldGhvZCA9ICdwZWFyc29uJykKICBwcmludChjb3JyZWxhdGlvbikKfQoKc2NhdCA8LSBnZ3Bsb3QocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLCBhZXMoeD1FVmNvdXBfcHJlZGljdCwgeT1EZWVwU2VxX3ByZWRpY3QsIGNvbG9yPWNhdGVnb3J5LCBhbHBoYT1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnksIHNoYXBlPWNhdGVnb3J5LCBzaXplPWNhdGVnb3J5KSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiY29tYmluYXRvcmlhbCI9ImRhc2hlZCIsICJzYXR1cmF0aW9uYWwiPSJ0d29kYXNoIikpICsgdGhlbWVfbGlnaHQoKSArIHhsYWIoIkVWY291cGxpbmdzIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgeWxhYigiRGVlcFNlcXVlbmNlIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX0RlZXBTZXFfRVZjb3VwLnBkZiIsIHNjYXQsIHdpZHRoPTIsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9EZWVwU2VxX0VWY291cC5wbmciLCBzY2F0LCB3aWR0aD0yLCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKYGBgCgojIyMgQ29tcGFyaXNvbiBFVm11dGF0aW9uLCBNU0EgVHJhbnNmb3JtZXIKCmBgYHtyfQpmb3IoY2F0IGluIGMoImNvbWJpbmF0b3JpYWwiLCAic2F0dXJhdGlvbmFsIikpewogIHByaW50KGNhdCkKICBzdWJzZXRfZm9yTG9vcCA8LSBzdWJzZXQocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLCByZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY2F0ZWdvcnk9PWNhdCAmIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjb25kaXRpb249PSJDTF9OMiIpCiAgcHJpbnQobnJvdyhzdWJzZXRfZm9yTG9vcCkpCiAgY29ycmVsYXRpb24gPC0gY29yLnRlc3Qoc3Vic2V0X2Zvckxvb3AkRVZjb3VwX3ByZWRpY3QsIHN1YnNldF9mb3JMb29wJE1TQV9UcmFuc2Zvcm0sIG1ldGhvZCA9ICdzcGVhcm1hbicpCiAgcHJpbnQoY29ycmVsYXRpb24pCiAgY29ycmVsYXRpb24gPC0gY29yLnRlc3Qoc3Vic2V0X2Zvckxvb3AkRVZjb3VwX3ByZWRpY3QsIHN1YnNldF9mb3JMb29wJE1TQV9UcmFuc2Zvcm0sIG1ldGhvZCA9ICdwZWFyc29uJykKICBwcmludChjb3JyZWxhdGlvbikKfQoKc2NhdCA8LSBnZ3Bsb3QocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLCBhZXMoeD1FVmNvdXBfcHJlZGljdCwgeT1NU0FfVHJhbnNmb3JtLCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5LCBzaGFwZT1jYXRlZ29yeSwgc2l6ZT1jYXRlZ29yeSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJkYXNoZWQiLCAic2F0dXJhdGlvbmFsIj0idHdvZGFzaCIpKSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJFVmNvdXBsaW5ncyBmaXRuZXNzIHByZWRpY3Rpb24iKSArIHlsYWIoIk1TQSBUcmFuc2Zvcm0gZml0bmVzcyBwcmVkaWN0aW9uIikgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NjYXR0ZXJfTVNBX1RyYW5zZm9ybV9FVmNvdXAucGRmIiwgc2NhdCwgd2lkdGg9MiwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX01TQV9UcmFuc2Zvcm1fRVZjb3VwLnBuZyIsIHNjYXQsIHdpZHRoPTIsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCiMjIyBDb21wYXJpc29uICJhZGRpdGl2ZSBzY29yZSIgYW5kIG5vcm1hbGl6ZWQgZml0bmVzcwoKIyMjIyBQZWFyc29uCgpgYGB7cn0KcmVkX2ZpdG5lc3Nfbm9OQV9hZGRpdGl2ZSA8LSBzdWJzZXQocmVkX2ZpdG5lc3MsICFpcy5uYShyZWRfZml0bmVzcyRhZGRpdGl2ZV9zY29yZSkpCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTk2OTk4NTgvZ2dwbG90LWFkZGluZy1yZWdyZXNzaW9uLWxpbmUtZXF1YXRpb24tYW5kLXIyLXdpdGgtZmFjZXQKbG1fZXFuID0gZnVuY3Rpb24oZGYpewogICAgbSA9IGxtKGFkZGl0aXZlX3Njb3JlIH4gbm9ybSwgZGYpOwogICAgY29yX2Zvcm0gPSBjb3IudGVzdChkZiRub3JtLCBkZiRhZGRpdGl2ZV9zY29yZSwgbWV0aG9kID0gJ3BlYXJzb24nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgiciA9In5yLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+ciAjUGVhcnNvbidzIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KCmVxIDwtIGRkcGx5KHJlZF9maXRuZXNzX25vTkFfYWRkaXRpdmUsLihjb25kaXRpb24pLGxtX2VxbikKCnAgPC0gZ2dwbG90KGRhdGEgPSByZWRfZml0bmVzc19ub05BX2FkZGl0aXZlLCBhZXMoeCA9IG5vcm0sIHkgPSBhZGRpdGl2ZV9zY29yZSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSwgc2hhcGU9cG9pbnRzaGFwZSkgKwogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LGxpbmV0eXBlPSJkYXNoZWQiLCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJBZGRpdGl2ZSBzY29yZSIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KQpzY2F0ID0gcCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gLTAuNCwgeSA9IDEuOCxsYWJlbD1WMSksIHNpemU9OCwgc2l6ZS51bml0PSJwdCIsIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9hZGRpdGl2ZV9jb21iaS5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9hZGRpdGl2ZV9jb21iaS5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCiMjIyMgU3BlYXJtYW4KCmBgYHtyfQpyZWRfZml0bmVzc19ub05BX2FkZGl0aXZlIDwtIHN1YnNldChyZWRfZml0bmVzcywgIWlzLm5hKHJlZF9maXRuZXNzJGFkZGl0aXZlX3Njb3JlKSkKIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xOTY5OTg1OC9nZ3Bsb3QtYWRkaW5nLXJlZ3Jlc3Npb24tbGluZS1lcXVhdGlvbi1hbmQtcjItd2l0aC1mYWNldApsbV9lcW4gPSBmdW5jdGlvbihkZil7CiAgICBtID0gbG0oYWRkaXRpdmVfc2NvcmUgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJGFkZGl0aXZlX3Njb3JlLCBtZXRob2QgPSAnc3BlYXJtYW4nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgicmhvID0ifnIsICNpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiIHIyPSJ+cjIqIiBwPSAifnAqIiwgUGVhcnNvbidzIHI9In5yICNQZWFyc29uJ3MgCiAgICAgICAgIGxpc3QoYSA9IGZvcm1hdChjb2VmKG0pW1sxXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgICBiID0gZm9ybWF0KGNvZWYobSlbWzJdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpLAogICAgICAgICAgICAgciA9IGZvcm1hdChjb3JfZm9ybSRlc3RpbWF0ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBwID0gZm9ybWF0KGNvcl9mb3JtJHAudmFsdWVbWzFdXSwgZGlnaXRzPTIpKSkKICAgIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSk7ICAgICAgICAgICAgICAgICAKfQoKZXEgPC0gZGRwbHkocmVkX2ZpdG5lc3Nfbm9OQV9hZGRpdGl2ZSwuKGNvbmRpdGlvbiksbG1fZXFuKQoKcCA8LSBnZ3Bsb3QoZGF0YSA9IHJlZF9maXRuZXNzX25vTkFfYWRkaXRpdmUsIGFlcyh4ID0gbm9ybSwgeSA9IGFkZGl0aXZlX3Njb3JlLCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnkpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemU9Y2F0ZWdvcnkpLCBzaGFwZT1wb2ludHNoYXBlKSArCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsbGluZXR5cGU9ImRhc2hlZCIsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIkFkZGl0aXZlIHNjb3JlIikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpCnNjYXQgPSBwICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAtMC40LCB5ID0gMS44LGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0IiwgcGFyc2UgPSBUUlVFLCBpbmhlcml0LmFlcz1GQUxTRSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX2FkZGl0aXZlX2NvbWJpX1NwZWFybWFuLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX2FkZGl0aXZlX2NvbWJpX1NwZWFybWFuLnBuZyIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmBgYAoKIyMjIENvbXBhcmlzb24gRGVlcFNlcXVlbmNlLCBub3JtYWxpemVkIGZpdG5lc3MKCmBgYHtyfQpsbV9lcW4gPSBmdW5jdGlvbihkZil7CiAgICBtID0gbG0oRGVlcFNlcV9wcmVkaWN0IH4gbm9ybSwgZGYpOwogICAgY29yX2Zvcm0gPSBjb3IudGVzdChkZiRub3JtLCBkZiREZWVwU2VxX3ByZWRpY3QsIG1ldGhvZCA9ICdwZWFyc29uJykKICAgIGVxIDwtIHN1YnN0aXR1dGUoInIgPSJ+cip+Y2F0MiwgI2l0YWxpYyh5KSA9PSBhICsgYiAlLiUgaXRhbGljKHgpKiIgcjI9In5yMioiIHA9ICJ+cCoiLCBQZWFyc29uJ3Mgcj0ifnIgI1BlYXJzb24ncyAKICAgICAgICAgbGlzdChhID0gZm9ybWF0KGNvZWYobSlbWzFdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgIGIgPSBmb3JtYXQoY29lZihtKVtbMl1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICByMiA9IGZvcm1hdChzdW1tYXJ5KG0pJHIuc3F1YXJlZCwgZGlnaXRzID0gMyksCiAgICAgICAgICAgICByID0gZm9ybWF0KGNvcl9mb3JtJGVzdGltYXRlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIHAgPSBmb3JtYXQoY29yX2Zvcm0kcC52YWx1ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBjYXQyID0gdW5pcXVlKGRmJGNhdGVnb3J5MikpKQogICAgYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKTsgICAgICAgICAgICAgICAgIAp9CnJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjYXRlZ29yeTIgPC0gcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJGNhdGVnb3J5CnJlZF9maXRuZXNzX25vTkFfRGVlcFNlcVtyZWRfZml0bmVzc19ub05BX0RlZXBTZXEkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kY2F0ZWdvcnkyIDwtICJzYXR1cmF0aW9uYWwiCmVxIDwtIGRkcGx5KHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwuKGNvbmRpdGlvbiwgY2F0ZWdvcnkyKSxsbV9lcW4pCgpyZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3IDwtIHN1YnNldChyZWRfZml0bmVzc19ub05BX0RlZXBTZXEsIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjYXRlZ29yeT09ImNvbWJpbmF0b3JpYWwiICYgIWdyZXBsKCJWMzM3IiwgcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJHNnUk5BX3RhcmdldCkpCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcV9vbmx5Q29tYl9ubzQzMzckbm9ybSwgcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNyREZWVwU2VxX3ByZWRpY3QsIG1ldGhvZCA9ICdzcGVhcm1hbicpCmNvcnJlbGF0aW9uCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcV9vbmx5Q29tYl9ubzQzMzckbm9ybSwgcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNyREZWVwU2VxX3ByZWRpY3QsIG1ldGhvZCA9ICdwZWFyc29uJykKY29ycmVsYXRpb24KCnNjYXQgPC0gZ2dwbG90KGRhdGEgPSByZWRfZml0bmVzc19ub05BX0RlZXBTZXEsIGFlcyh4ID0gbm9ybSwgeSA9IERlZXBTZXFfcHJlZGljdCwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIkRlZXBTZXF1ZW5jZSBwcmVkaWN0aW9uIikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X2FscGhhKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbWJpX3NhdCkgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJkYXNoZWQiLCAic2F0dXJhdGlvbmFsIj0idHdvZGFzaCIpKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQgPSBzY2F0ICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAwLjUsIHkgPS0xNSwgbGFiZWw9VjEpLCB2anVzdD1jKDAsMS4yLDAsMS4yLDAsMS4yKSwgcGFyc2UgPSBUUlVFLCBpbmhlcml0LmFlcz1GQUxTRSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0IikgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX0RlZXBTZXFfbm9ybS5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9EZWVwU2VxX25vcm0ucG5nIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKCnJlZF9maXRuZXNzX25vTkFfRGVlcFNlcV9vbmx5Q29tYl9ubzQzMzckY2F0ZWdvcnkyIDwtIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcV9vbmx5Q29tYl9ubzQzMzckY2F0ZWdvcnkKcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzN1tyZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3JHNnUk5BX3RhcmdldD09IldUIixdJGNhdGVnb3J5MiA8LSAic2F0dXJhdGlvbmFsIgplcSA8LSBkZHBseShyZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3LC4oY29uZGl0aW9uLCBjYXRlZ29yeTIpLGxtX2VxbikKCnNjYXQgPC0gZ2dwbG90KGRhdGEgPSByZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3LCBhZXMoeCA9IG5vcm0sIHkgPSBEZWVwU2VxX3ByZWRpY3QsIGNvbG9yPWNhdGVnb3J5LCBhbHBoYT1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnkyLCBzaGFwZT1jYXRlZ29yeSkpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZT1jYXRlZ29yeSkpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArIAogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJEZWVwU2VxdWVuY2UgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMTUsIGxhYmVsPVYxKSwgcGFyc2UgPSBUUlVFLCBpbmhlcml0LmFlcz1GQUxTRSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0IikgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX0RlZXBTZXFfbm9ybV9ub1YzMjMucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfRGVlcFNlcV9ub3JtX25vVjMyMy5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCiMjIyBDb21wYXJpc29uIEVWbXV0YXRpb24sIG5vcm1hbGl6ZWQgZml0bmVzcwoKYGBge3J9CnJlZF9maXRuZXNzX25vTkFfRVZjb3VwIDwtIHN1YnNldChyZWRfZml0bmVzcywgIWlzLm5hKHJlZF9maXRuZXNzJEVWY291cF9wcmVkaWN0KSkKCmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShFVmNvdXBfcHJlZGljdCB+IG5vcm0sIGRmKTsKICAgIGNvcl9mb3JtID0gY29yLnRlc3QoZGYkbm9ybSwgZGYkRVZjb3VwX3ByZWRpY3QsIG1ldGhvZCA9ICdwZWFyc29uJykKICAgIGVxIDwtIHN1YnN0aXR1dGUoInIgPSJ+cip+Y2F0MiwgI2l0YWxpYyh5KSA9PSBhICsgYiAlLiUgaXRhbGljKHgpKiIgcjI9In5yMioiIHA9ICJ+cCoiLCBQZWFyc29uJ3Mgcj0ifnIgI1BlYXJzb24ncyAKICAgICAgICAgbGlzdChhID0gZm9ybWF0KGNvZWYobSlbWzFdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgIGIgPSBmb3JtYXQoY29lZihtKVtbMl1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICByMiA9IGZvcm1hdChzdW1tYXJ5KG0pJHIuc3F1YXJlZCwgZGlnaXRzID0gMyksCiAgICAgICAgICAgICByID0gZm9ybWF0KGNvcl9mb3JtJGVzdGltYXRlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIHAgPSBmb3JtYXQoY29yX2Zvcm0kcC52YWx1ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBjYXQyID0gdW5pcXVlKGRmJGNhdGVnb3J5MikpKQogICAgYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKTsgICAgICAgICAgICAgICAgIAp9CnJlZF9maXRuZXNzX25vTkFfRVZjb3VwJGNhdGVnb3J5MiA8LSByZWRfZml0bmVzc19ub05BX0VWY291cCRjYXRlZ29yeQpyZWRfZml0bmVzc19ub05BX0VWY291cFtyZWRfZml0bmVzc19ub05BX0VWY291cCRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRjYXRlZ29yeTIgPC0gInNhdHVyYXRpb25hbCIKZXEgPC0gZGRwbHkocmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXAsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXBfbm80MzM3IDwtIHN1YnNldChyZWRfZml0bmVzc19ub05BX0VWY291cCwgcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXAkY2F0ZWdvcnk9PSJjb21iaW5hdG9yaWFsIiAmICFncmVwbCgiVjMzNyIsIHJlZF9maXRuZXNzX25vTkFfRVZjb3VwJHNnUk5BX3RhcmdldCkpCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHJlZF9maXRuZXNzX25vTkFfRVZjb3VwX25vNDMzNyRub3JtLCByZWRfZml0bmVzc19ub05BX0VWY291cF9ubzQzMzckRVZjb3VwX3ByZWRpY3QsIG1ldGhvZCA9ICdzcGVhcm1hbicpCmNvcnJlbGF0aW9uCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHJlZF9maXRuZXNzX25vTkFfRVZjb3VwX25vNDMzNyRub3JtLCByZWRfZml0bmVzc19ub05BX0VWY291cF9ubzQzMzckRVZjb3VwX3ByZWRpY3QsIG1ldGhvZCA9ICdwZWFyc29uJykKY29ycmVsYXRpb24KCnNjYXQgPC0gZ2dwbG90KGRhdGEgPSByZWRfZml0bmVzc19ub05BX0VWY291cCwgYWVzKHggPSBub3JtLCB5ID0gRVZjb3VwX3ByZWRpY3QsIGNvbG9yPWNhdGVnb3J5LCBhbHBoYT1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnkyLCBzaGFwZT1jYXRlZ29yeSkpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZT1jYXRlZ29yeSkpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIkVWbXV0YXRpb24gcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMTAsIGxhYmVsPVYxKSwgdmp1c3Q9YygwLDEuMiwwLDEuMiwwLDEuMiksIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UsIHNpemU9OCwgc2l6ZS51bml0ID0gInB0IikgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX25vcm1fYWxsX0VWY291cC5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9ub3JtX2FsbF9FVmNvdXAucG5nIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKCnJlZF9maXRuZXNzX25vTkFfRVZjb3VwX25vNDMzNyRjYXRlZ29yeTIgPC0gcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXBfbm80MzM3JGNhdGVnb3J5CnJlZF9maXRuZXNzX25vTkFfRVZjb3VwX25vNDMzN1tyZWRfZml0bmVzc19ub05BX0VWY291cF9ubzQzMzckc2dSTkFfdGFyZ2V0PT0iV1QiLF0kY2F0ZWdvcnkyIDwtICJzYXR1cmF0aW9uYWwiCmVxIDwtIGRkcGx5KHJlZF9maXRuZXNzX25vTkFfRVZjb3VwX25vNDMzNywuKGNvbmRpdGlvbiwgY2F0ZWdvcnkyKSxsbV9lcW4pCgpzY2F0IDwtIGdncGxvdChkYXRhID0gcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXBfbm80MzM3LCBhZXMoeCA9IG5vcm0sIHkgPSBFVmNvdXBfcHJlZGljdCwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsKICAgICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHhsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyIpICsgeWxhYigiRVZtdXRhdGlvbiBwcmVkaWN0aW9uIikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X2FscGhhKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbWJpX3NhdCkgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJkYXNoZWQiLCAic2F0dXJhdGlvbmFsIj0idHdvZGFzaCIpKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQgPSBzY2F0ICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAwLjUsIHkgPS0xMCwgbGFiZWw9VjEpLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFLCBzaXplPTgsIHNpemUudW5pdCA9ICJwdCIpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9ub3JtX2FsbF9FVmNvdXBfbm9WMzIzLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX25vcm1fYWxsX0VWY291cF9ub1YzMjMucG5nIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKYGBgCgojIyMgQ29tcGFyaXNvbiBNU0EgdHJhbnNmb3JtZXIsIG5vcm1hbGl6ZWQgZml0bmVzcwoKYGBge3J9CmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShNU0FfVHJhbnNmb3JtIH4gbm9ybSwgZGYpOwogICAgY29yX2Zvcm0gPSBjb3IudGVzdChkZiRub3JtLCBkZiRNU0FfVHJhbnNmb3JtLCBtZXRob2QgPSAncGVhcnNvbicpCiAgICBlcSA8LSBzdWJzdGl0dXRlKCJyID0ifnIqfmNhdDIsICNpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiIHIyPSJ+cjIqIiBwPSAifnAqIiwgUGVhcnNvbidzIHI9In5yICNQZWFyc29uJ3MgCiAgICAgICAgIGxpc3QoYSA9IGZvcm1hdChjb2VmKG0pW1sxXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgICBiID0gZm9ybWF0KGNvZWYobSlbWzJdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpLAogICAgICAgICAgICAgciA9IGZvcm1hdChjb3JfZm9ybSRlc3RpbWF0ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBwID0gZm9ybWF0KGNvcl9mb3JtJHAudmFsdWVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgY2F0MiA9IHVuaXF1ZShkZiRjYXRlZ29yeTIpKSkKICAgIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSk7ICAgICAgICAgICAgICAgICAKfQpyZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY2F0ZWdvcnkyIDwtIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjYXRlZ29yeQpyZWRfZml0bmVzc19ub05BX0RlZXBTZXFbcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJHNnUk5BX3RhcmdldD09IldUIixdJGNhdGVnb3J5MiA8LSAic2F0dXJhdGlvbmFsIgplcSA8LSBkZHBseShyZWRfZml0bmVzc19ub05BX0RlZXBTZXEsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwgYWVzKHggPSBub3JtLCB5ID0gTVNBX1RyYW5zZm9ybSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIk1TQVRyYW5zZm9ybWVyIChlbnNlbWJsZSkgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMTAsIGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0Iiwgdmp1c3Q9YygwLDEuMiwwLDEuMiwwLDEuMiksIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9NU0FfVHJhbnNmb3JtX25vcm0ucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfTVNBX1RyYW5zZm9ybV9ub3JtLnBuZyIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCgpyZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3JGNhdGVnb3J5MiA8LSByZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3JGNhdGVnb3J5CnJlZF9maXRuZXNzX25vTkFfRGVlcFNlcV9vbmx5Q29tYl9ubzQzMzdbcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNyRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRjYXRlZ29yeTIgPC0gInNhdHVyYXRpb25hbCIKZXEgPC0gZGRwbHkocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNywuKGNvbmRpdGlvbiwgY2F0ZWdvcnkyKSxsbV9lcW4pCgpzY2F0IDwtIGdncGxvdChkYXRhID0gcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNywgYWVzKHggPSBub3JtLCB5ID0gTVNBX1RyYW5zZm9ybSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIk1TQVRyYW5zZm9ybWVyIChlbnNlbWJsZSkgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMTAsIGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0IiwgcGFyc2UgPSBUUlVFLCBpbmhlcml0LmFlcz1GQUxTRSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX01TQV9UcmFuc2Zvcm1fbm9ybV9ub1YzMjMucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfTVNBX1RyYW5zZm9ybV9ub3JtX25vVjMyMy5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCiMjIyBDb21wYXJpc29uIFByb3RlaW5OUFQsIG5vcm1hbGl6ZWQgZml0bmVzcwoKYGBge3J9CmRmX3Byb3RlaW5OUFQgPC0gc3Vic2V0KHJlZF9maXRuZXNzLCAhaXMubmEocmVkX2ZpdG5lc3MkcHJvdGVpbk5QVF9wcmVkaWN0KSkKCmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShwcm90ZWluTlBUX3ByZWRpY3QgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJHByb3RlaW5OUFRfcHJlZGljdCwgbWV0aG9kID0gJ3BlYXJzb24nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgiciA9In5yKn5jYXQyLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+ciAjUGVhcnNvbidzIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIGNhdDIgPSB1bmlxdWUoZGYkY2F0ZWdvcnkyKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KZGZfcHJvdGVpbk5QVCRjYXRlZ29yeTIgPC0gZGZfcHJvdGVpbk5QVCRjYXRlZ29yeQpkZl9wcm90ZWluTlBUW2RmX3Byb3RlaW5OUFQkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kY2F0ZWdvcnkyIDwtICJzYXR1cmF0aW9uYWwiCmVxIDwtIGRkcGx5KGRmX3Byb3RlaW5OUFQsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IGRmX3Byb3RlaW5OUFQsIGFlcyh4ID0gbm9ybSwgeSA9IHByb3RlaW5OUFRfcHJlZGljdCwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIlByb3RlaW5OUFQgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMiwgbGFiZWw9VjEpLCBzaXplPTgsIHNpemUudW5pdD0icHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NjYXR0ZXJfcHJvdGVpbk5QVF9ub3JtLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX3Byb3RlaW5OUFRfbm9ybS5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQoKZGZfcHJvdGVpbk5QVF9ub1YzMjMgPC0gc3Vic2V0KGRmX3Byb3RlaW5OUFQsICFncmVwbCgiVjMzNyIsIGRmX3Byb3RlaW5OUFQkc2dSTkFfdGFyZ2V0KSkKCmRmX3Byb3RlaW5OUFRfbm9WMzIzJGNhdGVnb3J5MiA8LSBkZl9wcm90ZWluTlBUX25vVjMyMyRjYXRlZ29yeQpkZl9wcm90ZWluTlBUX25vVjMyM1tkZl9wcm90ZWluTlBUX25vVjMyMyRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRjYXRlZ29yeTIgPC0gInNhdHVyYXRpb25hbCIKZXEgPC0gZGRwbHkoZGZfcHJvdGVpbk5QVF9ub1YzMjMsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IGRmX3Byb3RlaW5OUFRfbm9WMzIzLCBhZXMoeCA9IG5vcm0sIHkgPSBwcm90ZWluTlBUX3ByZWRpY3QsIGNvbG9yPWNhdGVnb3J5LCBhbHBoYT1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnkyLCBzaGFwZT1jYXRlZ29yeSkpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZT1jYXRlZ29yeSkpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArIAogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJQcm90ZWluTlBUIHByZWRpY3Rpb24iKSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiY29tYmluYXRvcmlhbCI9ImRhc2hlZCIsICJzYXR1cmF0aW9uYWwiPSJ0d29kYXNoIikpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdCA9IHNjYXQgKyBnZW9tX3RleHQoZGF0YT1lcSxhZXMoeCA9IDAuNSwgeSA9LTIsIGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0IiwgcGFyc2UgPSBUUlVFLCBpbmhlcml0LmFlcz1GQUxTRSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX3Byb3RlaW5OUFRfbm9ybV9ub1YzMjMucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfcHJvdGVpbk5QVF9ub3JtX25vVjMyMy5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQoKCmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShwcm90ZWluTlBUX3ByZWRpY3QgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJHByb3RlaW5OUFRfcHJlZGljdCwgbWV0aG9kID0gJ3NwZWFybWFuJykKICAgIGVxIDwtIHN1YnN0aXR1dGUoInJobyA9In5yKn5jYXQyLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+ciAjUGVhcnNvbidzIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIGNhdDIgPSB1bmlxdWUoZGYkY2F0ZWdvcnkyKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KZGZfcHJvdGVpbk5QVCRjYXRlZ29yeTIgPC0gZGZfcHJvdGVpbk5QVCRjYXRlZ29yeQpkZl9wcm90ZWluTlBUW2RmX3Byb3RlaW5OUFQkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kY2F0ZWdvcnkyIDwtICJzYXR1cmF0aW9uYWwiCmVxIDwtIGRkcGx5KGRmX3Byb3RlaW5OUFQsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IGRmX3Byb3RlaW5OUFQsIGFlcyh4ID0gbm9ybSwgeSA9IHByb3RlaW5OUFRfcHJlZGljdCwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIlByb3RlaW5OUFQgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMiwgbGFiZWw9VjEpLCBzaXplPTgsIHNpemUudW5pdD0icHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NjYXR0ZXJfcHJvdGVpbk5QVF9ub3JtX3NwZWFybWFuLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX3Byb3RlaW5OUFRfbm9ybV9zcGVhcm1hbi5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCiMjIyBDb21wYXJpc29uIE1TQSB0cmFuc2Zvcm1lciwgUHJvdGVpbk5QVAoKYGBge3J9CnNjYXQgPC0gZ2dwbG90KGRmX3Byb3RlaW5OUFQsIGFlcyh4PXByb3RlaW5OUFRfcHJlZGljdCwgeT1NU0FfVHJhbnNmb3JtLCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5LCBzaGFwZT1jYXRlZ29yeSwgc2l6ZT1jYXRlZ29yeSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJkYXNoZWQiLCAic2F0dXJhdGlvbmFsIj0idHdvZGFzaCIpKSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJQcm90ZWluTlBUIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgeWxhYigiTVNBIFRyYW5zZm9ybSBmaXRuZXNzIHByZWRpY3Rpb24iKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X2FscGhhKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbWJpX3NhdCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX01TQV9UcmFuc2Zvcm1fcHJvdGVpbk5QVC5wZGYiLCBzY2F0LCB3aWR0aD0yLCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfTVNBX1RyYW5zZm9ybV9wcm90ZWluTlBULnBuZyIsIHNjYXQsIHdpZHRoPTIsIGhlaWdodD0yLCB1bml0cz0iaW4iKQoKc2NhdCA8LSBnZ3Bsb3QoZGZfcHJvdGVpbk5QVCwgYWVzKHg9cHJvdGVpbk5QVF9wcmVkaWN0LCB5PWFkZGl0aXZlX3Njb3JlLCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5LCBzaGFwZT1jYXRlZ29yeSwgc2l6ZT1jYXRlZ29yeSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJkYXNoZWQiLCAic2F0dXJhdGlvbmFsIj0idHdvZGFzaCIpKSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJQcm90ZWluTlBUIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgeWxhYigiQWRkaXRpdmUgc2NvcmUgZml0bmVzcyBwcmVkaWN0aW9uIikgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9hZGRpdGl2ZVNjb3JlX3Byb3RlaW5OUFQucGRmIiwgc2NhdCwgd2lkdGg9MiwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX2FkZGl0aXZlU2NvcmVfcHJvdGVpbk5QVC5wbmciLCBzY2F0LCB3aWR0aD0yLCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKYGBgCgojIyBDb3JyZWxhdGlvbiBjb25zZXJ2YXRpb24gdG8gZml0bmVzcwoKYGBge3J9CmNvcl9zdWJzIDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnk9PSJzYXR1cmF0aW9uYWwiIHwgZml0bmVzc19kYXRhJGNhdGVnb3J5PT0iY29tYmlBTkRzYXR1ciIpWyxjKCJhYV9wb3MiLCJub3JtIiwiY29uc2VydmF0aW9uU2NvcmUiLCJjYXRlZ29yeSIsImNvbmRpdGlvbiIpXSkKY29yX3N1YnNbY29yX3N1YnMkY2F0ZWdvcnk9PSJjb21iaUFORHNhdHVyIixdJGNhdGVnb3J5IDwtICJzYXR1cmF0aW9uYWwiCmNvcl9zdWJzIDwtIHN1YnNldChjb3Jfc3VicywgIWlzLm5hKGNvcl9zdWJzJGNvbnNlcnZhdGlvblNjb3JlKSkKY29yX3N1YnNfMiA8LSBjb3Jfc3VicyAlPiUgZHBseXI6Omdyb3VwX2J5KGFhX3BvcywgY29uZGl0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgLmdyb3Vwcz0ia2VlcCIsCiAgICBhdmVyYWdfZml0bmVzcz1tZWFuKG5vcm0pCiAgKQpjb3Jfc3VicyA8LSBsZWZ0X2pvaW4oY29yX3N1YnNfMixjb3Jfc3Vic1ssYygiYWFfcG9zIiwiY29uc2VydmF0aW9uU2NvcmUiLCJjYXRlZ29yeSIpXSkKCmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShjb25zZXJ2YXRpb25TY29yZSB+IGF2ZXJhZ19maXRuZXNzLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJGF2ZXJhZ19maXRuZXNzLCBkZiRjb25zZXJ2YXRpb25TY29yZSwgbWV0aG9kID0gJ3BlYXJzb24nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgiUGVhcnNvbidzIHIgPSJ+ciwgI2l0YWxpYyh5KSA9PSBhICsgYiAlLiUgaXRhbGljKHgpKiIgcjI9In5yMioiIHA9ICJ+cCoiLCBQZWFyc29uJ3Mgcj0ifnIKICAgICAgICAgbGlzdChhID0gZm9ybWF0KGNvZWYobSlbWzFdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgIGIgPSBmb3JtYXQoY29lZihtKVtbMl1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICByMiA9IGZvcm1hdChzdW1tYXJ5KG0pJHIuc3F1YXJlZCwgZGlnaXRzID0gMyksCiAgICAgICAgICAgICByID0gZm9ybWF0KGNvcl9mb3JtJGVzdGltYXRlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIHAgPSBmb3JtYXQoY29yX2Zvcm0kcC52YWx1ZVtbMV1dLCBkaWdpdHM9MikpKQogICAgYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKTsgICAgICAgICAgICAgICAgIAp9CmVxIDwtIGRkcGx5KGNvcl9zdWJzLC4oY29uZGl0aW9uKSxsbV9lcW4pCgpzY2F0IDwtIGdncGxvdChkYXRhID0gY29yX3N1YnMsIGFlcyh4ID0gYXZlcmFnX2ZpdG5lc3MsIHkgPSBjb25zZXJ2YXRpb25TY29yZSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeSkpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZT1jYXRlZ29yeSwgc2hhcGU9Y2F0ZWdvcnkpKSAgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIkNvbnNlcnZhdGlvbiBzY29yZSIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMSwgeSA9MC45LCBsYWJlbD1WMSksIHNpemU9OCwgc2l6ZS51bml0PSJwdCIsIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdAoKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2NvbnNlcnZhdGlvbl9ub3JtLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9jb25zZXJ2YXRpb25fbm9ybS5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCiMjIENvcnJlbGF0aW9uIHN1cmZhY2UgZXhwb3N1cmUgdG8gZml0bmVzcwoKYGBge3J9CmRvdHNpemVfc3VyZmFjZSA8LSAwLjQKc3VyZmFjZSA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJGNhdGVnb3J5PT0ic2F0dXJhdGlvbmFsIiB8IGZpdG5lc3NfZGF0YSRjYXRlZ29yeT09ImNvbWJpQU5Ec2F0dXIiKVssYygiYWFfcG9zIiwibm9ybSIsInJlbFNBUyIsICJkaW1lciIsImNhdGVnb3J5IiwiY29uZGl0aW9uIildKQpzdXJmYWNlW3N1cmZhY2UkY2F0ZWdvcnk9PSJjb21iaUFORHNhdHVyIixdJGNhdGVnb3J5IDwtICJzYXR1cmF0aW9uYWwiCnN1cmZhY2UgPC0gc3Vic2V0KHN1cmZhY2UsICFpcy5uYShzdXJmYWNlJHJlbFNBUykpCnN1cmZhY2VfMiA8LSBzdXJmYWNlICU+JSBkcGx5cjo6Z3JvdXBfYnkoYWFfcG9zLCBjb25kaXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpemUoCiAgICAuZ3JvdXBzPSJrZWVwIiwKICAgIGF2ZXJhZ19maXRuZXNzPW1lYW4obm9ybSkKICApCnN1cmZhY2UgPC0gbGVmdF9qb2luKHN1cmZhY2VfMixzdXJmYWNlWyxjKCJhYV9wb3MiLCJyZWxTQVMiLCAiZGltZXIiLCJjYXRlZ29yeSIpXSkKCmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShyZWxTQVMgfiBhdmVyYWdfZml0bmVzcywgZGYpOwogICAgY29yX2Zvcm0gPSBjb3IudGVzdChkZiRhdmVyYWdfZml0bmVzcywgZGYkcmVsU0FTLCBtZXRob2QgPSAncGVhcnNvbicpCiAgICBlcSA8LSBzdWJzdGl0dXRlKCJQZWFyc29uJ3MgciA9In5yLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+cgogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KZXEgPC0gZGRwbHkoc3VyZmFjZSwuKGNvbmRpdGlvbiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHN1cmZhY2UsIGFlcyh4ID0gYXZlcmFnX2ZpdG5lc3MsIHkgPSByZWxTQVMsIGNvbG9yPWRpbWVyKSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KHNpemU9ZG90c2l6ZV9zdXJmYWNlLCBzaGFwZT1wb2ludHNoYXBlLCBhbHBoYT1hbHBoYWxldmVsKSArCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV0eXBlPSJkYXNoZWQiLCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJSZWxhdGl2ZSBzb2x2ZW50IGFjY2Vzc2liaWxpdHkiKSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjOTNkZmZmZmYiKSwgbmEudmFsdWU9IiM3Nzc3NzciKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQgPSBzY2F0ICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAxLjAsIHkgPS0wLjA1LCBsYWJlbD1WMSksIHNpemU9OCwgc2l6ZS51bml0PSJwdCIsIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvcmVsU0FTX25vcm0ucGRmIiwgc2NhdCwgd2lkdGg9NywgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9yZWxTQVNfbm9ybS5wbmciLCBzY2F0LCB3aWR0aD0zMCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQoKCnN1cmZhY2Vfbm9EaW1lciA8LSBzdWJzZXQoc3VyZmFjZSwgaXMubmEoc3VyZmFjZSRkaW1lcikpCmVxIDwtIGRkcGx5KHN1cmZhY2Vfbm9EaW1lciwuKGNvbmRpdGlvbiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHN1cmZhY2Vfbm9EaW1lciwgYWVzKHggPSBhdmVyYWdfZml0bmVzcywgeSA9IHJlbFNBUywgY29sb3I9ZGltZXIpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZT1kb3RzaXplX3N1cmZhY2UsIHNoYXBlPXBvaW50c2hhcGUsIGFscGhhPWFscGhhbGV2ZWwpICsKICAgICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXR5cGU9ImRhc2hlZCIsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIlJlbGF0aXZlIHNvbHZlbnQgYWNjZXNzaWJpbGl0eSIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiM5M2RmZmZmZiIpLCBuYS52YWx1ZT0iIzc3Nzc3NyIpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdCA9IHNjYXQgKyBnZW9tX3RleHQoZGF0YT1lcSxhZXMoeCA9IDEuMCwgeSA9LTAuMDUsIGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQgPSAicHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3JlbFNBU19ub3JtX25vRGltZXIucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3JlbFNBU19ub3JtX25vRGltZXIucG5nIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKCgpzdXJmYWNlX0RpbWVyIDwtIHN1YnNldChzdXJmYWNlLCAhaXMubmEoc3VyZmFjZSRkaW1lcikpCmVxIDwtIGRkcGx5KHN1cmZhY2VfRGltZXIsLihjb25kaXRpb24pLGxtX2VxbikKCnNjYXQgPC0gZ2dwbG90KGRhdGEgPSBzdXJmYWNlX0RpbWVyLCBhZXMoeCA9IGF2ZXJhZ19maXRuZXNzLCB5ID0gcmVsU0FTLCBjb2xvcj1kaW1lcikpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChzaXplPWRvdHNpemVfc3VyZmFjZSwgc2hhcGU9cG9pbnRzaGFwZSwgYWxwaGE9YWxwaGFsZXZlbCkgKwogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ldHlwZT0iZGFzaGVkIiwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHhsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyIpICsgeWxhYigiUmVsYXRpdmUgc29sdmVudCBhY2Nlc3NpYmlsaXR5IikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzkzZGZmZmZmIiksIG5hLnZhbHVlPSIjNzc3Nzc3IikgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMS4wLCB5ID0tMC4wNSwgbGFiZWw9VjEpLCBzaXplPTgsIHNpemUudW5pdD0icHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3JlbFNBU19ub3JtX29ubHlEaW1lci5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvcmVsU0FTX25vcm1fb25seURpbWVyLnBuZyIsIHNjYXQsIHdpZHRoPTMwLCBoZWlnaHQ9MTgsIHVuaXRzPSJjbSIpCmBgYAoKIyBDb21wYXJpbmcgZGlmZmVyZW50IGNvbmRpdGlvbnMKCkEgY29tcGFyaXNvbiBiZXR3ZWVuIGRpZmZlcmVudCBjb25kaXRpb25zIGhlbHBzIHRvIGlkZW50aWZ5IHZhcmlhbnRzIHdoaWNoIGJlaGF2ZSBkaWZmZXJlbnQgaW4gZGlmZmVyZW50IGNvbmRpdGlvbnMsIGUuZy4gYmVjYXVzZSBvZiBhbiBlbmhhbmNlZCBzcGVjaWZpY2l0eSBvciBoaWdoZXIgY29tcGF0aWJpbGl0eSB3aXRoIGxpZ2h0LWRhcmsgY3ljbGVzLgoKIyMgQ29tcGFyZSBOMiBhbmQgTzIgZ2FzIGZlZWRzCgpgYGB7ciBmaXRuZXNzLWZpdG5lc3MtcGxvdHMtTjItTzJ9CnJlZF9maXRuZXNzIDwtIHVuaXF1ZShmaXRuZXNzX2RhdGFbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIiwgImNhdGVnb3J5IildKQp3aWRlX2ZpdG5lc3MgPC0gcGl2b3Rfd2lkZXIocmVkX2ZpdG5lc3MsIHZhbHVlc19mcm9tID1jKG5vcm0scF9maXRfYWRqX1dUKSwgbmFtZXNfZnJvbT1jb25kaXRpb24pCndpZGVfZml0bmVzcyRzZ1JOQV90YXJnZXQgPC0gZmFjdG9yKHdpZGVfZml0bmVzcyRzZ1JOQV90YXJnZXQsIGxldmVscz1jKHVuaXF1ZShzdWJzZXQod2lkZV9maXRuZXNzLCB3aWRlX2ZpdG5lc3Mkc2dSTkFfdGFyZ2V0ICE9ICJXVCIpKSRzZ1JOQV90YXJnZXQsICJXVCIpKQp3aWRlX2ZpdG5lc3MkV1Rfbm90X1dUIDwtICJub3RXVCIKd2lkZV9maXRuZXNzW3dpZGVfZml0bmVzcyRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRXVF9ub3RfV1QgPC0gIldUIgoKIyBzZXQgc29tZSBwbG90dGluZyBwYXJhbWV0ZXJzCldUX3NoYXBlIDwtIGMoIm5vdFdUIj0xNiwgIldUIj00KQpXVF9hbHBoYSA8LSBjKCJub3RXVCI9MC4yLCAiV1QiPTEuMCkKV1Rfc2l6ZSA8LSBjKCJub3RXVCI9MC4zLCAiV1QiPTAuNSkKQ0xPMl9DTE4yX2N1dG9mZiA8LSAwLjI1CmJhcmNvZGVfY3V0b2ZmIDwtIDIKYWRqcF9jdXRvZmYgPC0gMC4wNQoKIyBleHRyYWN0IHN1YnNldHMgZnJvbSBiaWcgZGF0YSBmcmFtZXMgdG8gdGVzdCBpZiBzaWduaWZpY2FudCBkaWZmZXJlbmNlcwpzdWJzZXRfc2lnbmlmX2hpZ2hPMiA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgc3Vic2V0KHdpZGVfZml0bmVzcywgd2lkZV9maXRuZXNzJG5vcm1fQ0xfTzI+MS4wICYgd2lkZV9maXRuZXNzJHBfZml0X2Fkal9XVF9DTF9PMjwwLjA1KSRzZ1JOQV90YXJnZXQpCnN1YnNldF9zaWduaWZfaGlnaExEIDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBzdWJzZXQod2lkZV9maXRuZXNzLCB3aWRlX2ZpdG5lc3Mkbm9ybV9MRCA+IHdpZGVfZml0bmVzcyRub3JtX0NMX08yICYgd2lkZV9maXRuZXNzJG5vcm1fTEQgPiAwLjkpJHNnUk5BX3RhcmdldCkKCiMgcHJlcGFyZSBwbG90dGluZywgY2FsY3VsYXRlIGxtIHRvIGNoZWNrIGRpc3RhbmNlIGZyb20gaXQgKGFzc3VtaW5nIGxpbmVhciByZWdyZXNzaW9uIGdpdmVzIGJldHRlciAxOjEgdGhhbiBsaW5lIHRocm91Z2ggb3JpZ2luKQpsbV9DTE8yX0NMTjIgPC0gbG0obm9ybV9DTF9OMiB+IG5vcm1fQ0xfTzIsIHdpZGVfZml0bmVzcykKc3VtbWFyeShsbV9DTE8yX0NMTjIpCgpjb3JyZWxhdGlvbiA8LSBjb3IudGVzdCh3aWRlX2ZpdG5lc3Mkbm9ybV9DTF9PMiwgd2lkZV9maXRuZXNzJG5vcm1fQ0xfTjIsIG1ldGhvZCA9ICdzcGVhcm1hbicpCmNvcnJlbGF0aW9uCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHdpZGVfZml0bmVzcyRub3JtX0NMX08yLCB3aWRlX2ZpdG5lc3Mkbm9ybV9DTF9OMiwgbWV0aG9kID0gJ3BlYXJzb24nKQpjb3JyZWxhdGlvbgoKd2lkZV9maXRuZXNzX0NMX09MIDwtIHdpZGVfZml0bmVzcwp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGlzdGFuY2VfbG0gPC0gd2lkZV9maXRuZXNzX0NMX09MJG5vcm1fQ0xfTjIgLSAobG1fQ0xPMl9DTE4yJGNvZWZmaWNpZW50c1sxXSArIGxtX0NMTzJfQ0xOMiRjb2VmZmljaWVudHNbMl0gKiB3aWRlX2ZpdG5lc3NfQ0xfT0wkbm9ybV9DTF9PMikKd2lkZV9maXRuZXNzX0NMX09MW29yZGVyKHdpZGVfZml0bmVzc19DTF9PTCRkaXN0YW5jZV9sbSwgZGVjcmVhc2luZyA9IEZBTFNFKSxdWzE6MTAsXQoKIyBjb2xvcmluZyBhY2NvcmRpbmcgdG8gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24Kd2lkZV9maXRuZXNzX0NMX09MJGRpZmYgPC0gIk5PIgp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfQ0xfT0wkZGlzdGFuY2VfbG0gPiAwXSA8LSAiQ0xfTjIiCndpZGVfZml0bmVzc19DTF9PTCRkaWZmW3dpZGVfZml0bmVzc19DTF9PTCRkaXN0YW5jZV9sbSA8IDBdIDwtICJDTF9PMiIKI3dpZGVfZml0bmVzc19DTF9PTCRkaWZmW3dpZGVfZml0bmVzc19DTF9PTCRkaXN0YW5jZV9sbSA+IENMTzJfQ0xOMl9jdXRvZmZdIDwtICJDTF9OMiIgIyBpbiBjYXNlIHdlIGNhcmUgZm9yIGN1dC1vZmYgZm9yIG1pbmltYWwgZGlzdGFuY2UgZnJvbSBsaW5lYXIgcmVncmVzc2lvbgojd2lkZV9maXRuZXNzX0NMX09MJGRpZmZbd2lkZV9maXRuZXNzX0NMX09MJGRpc3RhbmNlX2xtIDwgKC1DTE8yX0NMTjJfY3V0b2ZmKV0gPC0gIkNMX08yIiAjIGluIGNhc2Ugd2UgY2FyZSBmb3IgY3V0LW9mZiBmb3IgbWluaW1hbCBkaXN0YW5jZSBmcm9tIGxpbmVhciByZWdyZXNzaW9uCndpZGVfZml0bmVzc19DTF9PTFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kZGlmZiA8LSAiV1QiCmRvdHBsb3RfY29sb3JzIDwtIGMoY29sX2NvbmRpdGlvbnMsICJOTyI9IiNkM2QzZDNiMiIsICJvcmFuZ2UiLCAiV1QiPSJibGFjayIpCgojIHByZXBhcmUgbGFiZWxzIGZvciBwbG90CndpZGVfZml0bmVzc19DTF9PTCRkZWxhYmVsIDwtIE5BCndpZGVfZml0bmVzc19DTF9PTCRkZWxhYmVsW3dpZGVfZml0bmVzc19DTF9PTCRkaWZmICE9Ik5PIl0gPC0gYXMuY2hhcmFjdGVyKHdpZGVfZml0bmVzc19DTF9PTCRzZ1JOQV90YXJnZXRbd2lkZV9maXRuZXNzX0NMX09MJGRpZmYgIT0gIk5PIl0pCgpwIDwtIGdncGxvdCh3aWRlX2ZpdG5lc3NfQ0xfT0wsIGFlcyh4PW5vcm1fQ0xfTzIsIHk9bm9ybV9DTF9OMiwgY29sb3I9ZGlmZiwgc2hhcGU9V1Rfbm90X1dUKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPVdUX25vdF9XVCwgYWxwaGE9V1Rfbm90X1dUKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9V1Rfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPVdUX2FscGhhKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHk9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBhdCBjb250aW51b3VzIGxpZ2h0LCA1JSBDTzIsIDk1JSBOMiwgMCUgTzIiLCB4PSJXZWlnaHRlZCBtZWFuIGZpdG5lc3MgdmFsdWUgYXQgY29udGludW91cyBsaWdodCwgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0NMTjIkY29lZmZpY2llbnRzWzFdLHNsb3BlPWxtX0NMTzJfQ0xOMiRjb2VmZmljaWVudHNbMl0sbGluZXR5cGU9ImRhc2hlZCIsY29sb3I9ImJsYWNrIiwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gZG90cGxvdF9jb2xvcnMpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICMrIHhsaW0oLTYsICs3KSAreWxpbSgtNiwrNykKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvd2ZpdG5lc3NfQ0xOMl9DTE8yX3dpdGhvdXRTaWduaWZfd28tbGFiZWxzLnBkZiIsIHBsb3Q9cCwgd2lkdGg9NCwgaGVpZ2h0PTQsIHVuaXRzPSJjbSIpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dmaXRuZXNzX0NMTjJfQ0xPMl93aXRob3V0U2lnbmlmX3dvLWxhYmVscy5wbmciLCBwbG90PXAsIHdpZHRoPTQsIGhlaWdodD00LCB1bml0cz0iY20iKQpwCmBgYAoKVXNlIFdpbGNveG9uIHNpZ25lZCByYW5rIGV4YWN0IHRlc3QgdG8gY2hlY2sgaWYgYmFyY29kZXMgZm9yIHRoZSBzYW1lIHZhcmlhbnQgYmVoYXZlIHNpZ25pZmljaWFudGx5IGRpZmZlcmVudCBiZXR3ZWVuIHRoZSB0d28gdGVzdGVkIGNvbmRpdGlvbnMuCgpgYGB7ciB3aWxjb3hvbi1OMi1PMn0Kc3Vic2V0X3NpZ25pZl9oaWdoTzIgPC0gc3Vic2V0KHN1YnNldF9zaWduaWZfaGlnaE8yLCBzdWJzZXRfc2lnbmlmX2hpZ2hPMiRjb25kaXRpb24gJWluJSBjKCJDTF9OMiIsICJDTF9PMiIpKQoKZ2V0X2NvbnRyb2xzIDwtIGZ1bmN0aW9uKGNvbmRfc3BlYywgc2dSTkFfc3BlYyl7CiAgY29udHJvbF90YWJsZSA8LSBzdWJzZXRfc2lnbmlmX2hpZ2hPMltzdWJzZXRfc2lnbmlmX2hpZ2hPMiRjb25kaXRpb24gIT0gdW5pcXVlKGNvbmRfc3BlYykgJiBzdWJzZXRfc2lnbmlmX2hpZ2hPMiRzZ1JOQV90YXJnZXQgPT0gdW5pcXVlKHNnUk5BX3NwZWMpICYgc3Vic2V0X3NpZ25pZl9oaWdoTzIkdGltZSA9PSAwLF0KICBjb250cm9sX3RhYmxlJGZpdG5lc3MKfQoKc3Vic2V0X3NpZ25pZl9oaWdoTzIgPC0gZHBseXI6OmxlZnRfam9pbigKICBzdWJzZXRfc2lnbmlmX2hpZ2hPMiwKICBzdWJzZXRfc2lnbmlmX2hpZ2hPMiAlPiUKICAgIGRwbHlyOjpncm91cF9ieShzZ1JOQV90YXJnZXQsIGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKAogICAgICAuZ3JvdXBzID0gImtlZXAiLAogICAgICAjIGFwcGx5IFdpbGNveG9uIHJhbmsgc3VtIHRlc3QgYWdhaW5zdCBvdGhlciBjb25kaXRpb24KICAgICAgcF9maXRuZXNzX2NvbmRpdGlvbiA9IHN0YXRzOjp3aWxjb3gudGVzdCgKICAgICAgICB4ID0gZml0bmVzcywKICAgICAgICB5ID0gZ2V0X2NvbnRyb2xzKGNvbmRpdGlvbiwgc2dSTkFfdGFyZ2V0KSwKICAgICAgICBwYWlyZWQgPSBUUlVFLAogICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIKICAgICAgICApJHAudmFsdWUKICAgICAgKSwKICBieSA9IGMoInNnUk5BX3RhcmdldCIsICJjb25kaXRpb24iLCAidGltZSIpCiAgKSAKCnN1YnNldF9zaWduaWZfaGlnaE8yIDwtIHN1YnNldF9zaWduaWZfaGlnaE8yICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgbXV0YXRlKAogICAgcF9maXRuZXNzX2NvbmRpdGlvbl9hZGogPSBzdGF0czo6cC5hZGp1c3QocF9maXRuZXNzX2NvbmRpdGlvbiwgbWV0aG9kID0gIkJIIikKICAgICkKYGBgCgpQbG90IHBhcnQgb2YgZGF0YSBzZXQgd2hpY2ggd2FzIHRlc3RlZCBmb3Igc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgYW5kIGhpZ2hsaWdodCB2YXJpYW50cyB3aGljaCB3ZXJlIGZvdW5kIHRvIGJlIHNpZ25pZmljYW50LgoKYGBge3IgZml0bmVzcy1maXRuZXNzLXBsb3QtV2lsY294b24tc2lnbmlmaWNhbnR9CnN1YnNldF9zaWduaWZfaGlnaE8yX3JlZCA8LSB1bmlxdWUoc3Vic2V0X3NpZ25pZl9oaWdoTzJbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiLCAicF9maXRuZXNzX2NvbmRpdGlvbl9hZGoiKV0pCnN1YnNldF9zaWduaWZfaGlnaE8yX3JlZCA8LSBwaXZvdF93aWRlcihzdWJzZXRfc2lnbmlmX2hpZ2hPMl9yZWQsIHZhbHVlc19mcm9tID1jKG5vcm0scF9maXRuZXNzX2NvbmRpdGlvbl9hZGopLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKCiMgYWxwaGEsIHNpemUgYW5kIGxhYmVscyBhY2NvcmRpbmcgdG8gc2lnbmlmaWNhbmNlCndpZGVfZml0bmVzc19DTF9PTCRzaWduaWYgPC0gIk5PIgp3aWRlX2ZpdG5lc3NfQ0xfT0wkc2lnbmlmW3dpZGVfZml0bmVzc19DTF9PTCRzZ1JOQV90YXJnZXQgJWluJSB1bmlxdWUoc3Vic2V0KHN1YnNldF9zaWduaWZfaGlnaE8yLCBzdWJzZXRfc2lnbmlmX2hpZ2hPMiRwX2ZpdG5lc3NfY29uZGl0aW9uX2FkaiA8IGFkanBfY3V0b2ZmKSkkc2dSTkFfdGFyZ2V0XSA8LSAiU0lHIgp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGVsYWJlbCA8LSBOQQp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGVsYWJlbFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2lnbmlmID09IlNJRyJdIDwtIGFzLmNoYXJhY3Rlcih3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0W3dpZGVfZml0bmVzc19DTF9PTCRzaWduaWYgPT0iU0lHIl0pCndpZGVfZml0bmVzc19DTF9PTFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kc2lnbmlmIDwtICJXVCIgIyB0byBlbnN1cmUgV1QgaXMgdmlzaWJsZQpzaWduaWZfc2l6ZSA8LSBjKCJOTyI9MC4zLCAiU0lHIj0wLjUsICJXVCI9MC41KQpzaWduaWZfYWxwaGEgPC0gYygiTk8iPTAuMiwgIlNJRyI9MC45LCAiV1QiPTEuMCkKCnAgPC0gZ2dwbG90KHdpZGVfZml0bmVzc19DTF9PTCwgYWVzKHg9bm9ybV9DTF9PMiwgeT1ub3JtX0NMX04yLCBjb2xvcj1kaWZmLCBsYWJlbD1kZWxhYmVsLCBzaGFwZT1XVF9ub3RfV1QpKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9V1Rfc2hhcGUpICsgZ2VvbV9wb2ludChhZXMoc2l6ZT1zaWduaWYsIGFscGhhPXNpZ25pZiksIHNob3cubGVnZW5kPUZBTFNFKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1zaWduaWZfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPXNpZ25pZl9hbHBoYSkgKyB0aGVtZV9saWdodCgpICsgbGFicyh5PSJXZWlnaHRlZCBtZWFuIGZpdG5lc3MgdmFsdWUgYXQgY29udGludW91cyBsaWdodCwgNSUgQ08yLCA5NSUgTjIsIDAlIE8yIiwgeD0iV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIHZhbHVlIGF0IGNvbnRpbnVvdXMgbGlnaHQsIDUlIENPMiwgNzUlIE4yLCAyMCUgTzIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9DTE4yJGNvZWZmaWNpZW50c1sxXSxzbG9wZT1sbV9DTE8yX0NMTjIkY29lZmZpY2llbnRzWzJdLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJibGFjayIsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGRvdHBsb3RfY29sb3JzKSArIGdlb21fdGV4dF9yZXBlbChmb250ZmFjZT0iaXRhbGljIikgKyB4bGltKDAuNzUsIDIuNSkgK3lsaW0oMC43NSwxLjc1KQpwCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3dmaXRuZXNzX0NMTjJfQ0xPMl9XaWxjb3hfc2lnbmlmLnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTIuNSwgaGVpZ2h0PTEyLjUsIHVuaXRzPSJjbSIpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dmaXRuZXNzX0NMTjJfQ0xPMl9XaWxjb3hfc2lnbmlmLnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTIuNSwgaGVpZ2h0PTEyLjUsIHVuaXRzPSJjbSIpCgp3aWRlX2ZpdG5lc3NfQ0xfT0xbd2lkZV9maXRuZXNzX0NMX09MJHNpZ25pZj09IlNJRyIsXVtvcmRlcih3aWRlX2ZpdG5lc3NfQ0xfT0xbd2lkZV9maXRuZXNzX0NMX09MJHNpZ25pZj09IlNJRyIsXSRub3JtX0NMX08yLCBkZWNyZWFzaW5nPVRSVUUpLF0KYGBgCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzIDwtIGMoIkgzNThJIiwgIkszNjBJIiwgIk0zMjhJIiwgIlkzMzNUIiwgIkgzNThRIiwgIlkzMjJNIiwgIkQzMjlUIiwgIlkzMzNWIiwgIks5OUUiLCAiTTIzMUYiLCAiWTMzM0kiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KQpwb3NzaWJseV9zcGVjaWZpY192YXJpYW50cyA8LSBjKCJIMzU4SSIsICJLMzYwSSIsICJNMzI4SSIsICJZMzMzVCIsICJZMzIyTSIsICJEMzI5VCIsICJLOTlFIiwgIk0yMzFGIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSDM1OEkiPSIjMzMyMjg4ZmYiLCAiSzM2MEkiPSIjMTE3NzMzZmYiLCAiTTMyOEkiPSIjNDRhYTk5ZmYiLCAiWTMzM1QiPSIjODhjY2VlZmYiLCAiWTMyMk0iPSIjZGRjYzc3ZmYiLCAiRDMyOVQiPSIjY2M2Njc3ZmYiLCAiSzk5RSI9IiNhYTQ0OTlmZiIsICJNMjMxRiI9IiM4ODIyNTVmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwb3NzaWJseV9zcGVjaWZpY19zdWJzZXQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBwb3NzaWJseV9zcGVjaWZpY192YXJpYW50cykpCnBvc3NpYmx5X3NwZWNpZmljX3N1YnNldCRzZ1JOQV90YXJnZXQgPC0gZmFjdG9yKHBvc3NpYmx5X3NwZWNpZmljX3N1YnNldCRzZ1JOQV90YXJnZXQsIGxldmVscz1jKHVuaXF1ZShzdWJzZXQocG9zc2libHlfc3BlY2lmaWNfc3Vic2V0LCAhcG9zc2libHlfc3BlY2lmaWNfc3Vic2V0JHNnUk5BX3RhcmdldCAlaW4lIGMobmVnX2NvbnRyb2xfSzIxNCwgIldUIikpJHNnUk5BX3RhcmdldCksIG5lZ19jb250cm9sX0syMTQsICJXVCIpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocG9zc2libHlfc3BlY2lmaWNfc3Vic2V0KSArIGxhYnModGl0bGU9Im1vc3QgZGlmZmVyZW50IHZhcmlhbnRzIE4yLCBPMiBmZWVkcyIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICMrIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiV1QiPSJzb2xpZCIsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzldPTQ0LCBwb3NzaWJseV9zcGVjaWZpY192YXJpYW50c1sxXT04OCwgcG9zc2libHlfc3BlY2lmaWNfdmFyaWFudHNbMl09MTMsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzNdPTEzNDMsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzRdPTEzMTM0MywgcG9zc2libHlfc3BlY2lmaWNfdmFyaWFudHNbNV09NzMsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzZdPTIyNjIsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzddPTExMjIzMywgcG9zc2libHlfc3BlY2lmaWNfdmFyaWFudHNbOF09MTEyMjMzNDQpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9tb3N0RGlmZmVyZW50X0NMTjJfQ0xPMl90aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wLCB3aWR0aD0xMCwgaGVpZ2h0PTEwKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvbW9zdERpZmZlcmVudF9DTE4yX0NMTzJfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTAsIGhlaWdodD0xMCkKYSA8LSBwaXZvdF93aWRlcih1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzKVssYygic2dSTkFfdGFyZ2V0IiwgImNvbmRpdGlvbiIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pLCB2YWx1ZXNfZnJvbT1jKCJub3JtIiwgInBfZml0X2Fkal9XVCIpLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKYSRub3JtX211bHRpcGx5IDwtIGEkbm9ybV9DTF9OMiAqIGEkbm9ybV9DTF9PMiAqIGEkbm9ybV9MRAphW29yZGVyKGEkbm9ybV9tdWx0aXBseSwgZGVjcmVhc2luZyA9IFRSVUUpLF0KYGBgCgojIyBDb21wYXJlIGxpZ2h0LWRhcmsgYW5kIGNvbnRpbnVvdXMgbGlnaHQgYXQgdGhlIHNhbWUgZ2FzIGZlZWQKCmBgYHtyIGZpdG5lc3MtZml0bmVzcy1wbG90cy1PMi1MRH0KbG1fQ0xPMl9MRCA8LSBsbShub3JtX0xEIH4gbm9ybV9DTF9PMiwgd2lkZV9maXRuZXNzKQpzdW1tYXJ5KGxtX0NMTzJfTEQpCgpjb3JyZWxhdGlvbiA8LSBjb3IudGVzdCh3aWRlX2ZpdG5lc3Mkbm9ybV9DTF9PMiwgd2lkZV9maXRuZXNzJG5vcm1fTEQsIG1ldGhvZCA9ICdzcGVhcm1hbicpCmNvcnJlbGF0aW9uCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHdpZGVfZml0bmVzcyRub3JtX0NMX08yLCB3aWRlX2ZpdG5lc3Mkbm9ybV9MRCwgbWV0aG9kID0gJ3BlYXJzb24nKQpjb3JyZWxhdGlvbgoKQ0xfTERfY3V0b2ZmIDwtIDAuMzIKYmFyY29kZV9jdXRvZmYgPC0gMgphZGpwX2N1dG9mZiA8LSAwLjA1Cgp3aWRlX2ZpdG5lc3NfTERfT0wgPC0gd2lkZV9maXRuZXNzCgp3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPC0gd2lkZV9maXRuZXNzX0xEX09MJG5vcm1fTEQgLSAobG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0gKyBsbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSAqIHdpZGVfZml0bmVzc19MRF9PTCRub3JtX0NMX08yKQp3aWRlX2ZpdG5lc3NfTERfT0xbb3JkZXIod2lkZV9maXRuZXNzX0xEX09MJGRpc3RhbmNlX2xtLCBkZWNyZWFzaW5nID0gVFJVRSksXVsxOjEwLF0KCiMgY29sb3JpbmcgYWNjb3JkaW5nIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCndpZGVfZml0bmVzc19MRF9PTCRkaWZmIDwtICJOTyIKd2lkZV9maXRuZXNzX0xEX09MJGRpZmZbd2lkZV9maXRuZXNzX0xEX09MJGRpc3RhbmNlX2xtID4gMF0gPC0gIkxEIgp3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPCAwXSA8LSAiQ0xfTzIiCiN3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPiBDTF9MRF9jdXRvZmZdIDwtICJMRCIgIyBpZiB1c2luZyBjdXQtb2ZmCiN3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPCAoLUNMX0xEX2N1dG9mZildIDwtICJDTF9PMiIgIyBpZiB1c2luZyBjdXQtb2ZmCndpZGVfZml0bmVzc19MRF9PTFt3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kZGlmZiA8LSAiV1QiCgpwIDwtIGdncGxvdCh3aWRlX2ZpdG5lc3NfTERfT0wsIGFlcyh4PW5vcm1fQ0xfTzIsIHk9bm9ybV9MRCwgY29sb3I9ZGlmZiwgc2hhcGU9V1Rfbm90X1dUKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPVdUX25vdF9XVCwgYWxwaGE9V1Rfbm90X1dUKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9V1Rfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPVdUX2FscGhhKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHk9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBpbiBsaWdodC1kYXJrIGN5Y2xlcywgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiIsIHg9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBpbiBjb250aW51b3VzIGxpZ2h0LCA1JSBDTzIsIDc1JSBOMiwgMjAlIE8yIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0PWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzFdLHNsb3BlPWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzJdLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJibGFjayIpKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGRvdHBsb3RfY29sb3JzKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSAjKyB4bGltKC02LCArNykgK3lsaW0oLTYsKzcpICArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1sxXS1DTF9MRF9jdXRvZmYsIHNsb3BlPWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzJdLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9ImJsYWNrIikgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0rQ0xfTERfY3V0b2ZmLCBzbG9wZT1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yPSJibGFjayIpIApnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi93Zml0bmVzc19DTE8yX0xEX3dpdGhvdXRTaWduaWZfd2l0aG91dExhYmVsaW5nLnBkZiIsIHBsb3Q9cCwgd2lkdGg9NCwgaGVpZ2h0PTQsIHVuaXRzPSJjbSIpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dmaXRuZXNzX0NMTzJfTERfd2l0aG91dFNpZ25pZl93aXRob3V0TGFiZWxpbmcucG5nIiwgcGxvdD1wLCB3aWR0aD00LCBoZWlnaHQ9NCwgdW5pdHM9ImNtIikKcApgYGAKClJ1biBXaWxjb3hvbiB0ZXN0IHRvIGNoZWNrIGZvciBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGNvbmRpdGlvbnMuCgpgYGB7ciB3aWxjb3hvbi1MRC1PMn0Kc3Vic2V0X3NpZ25pZl9oaWdoTEQgPC0gc3Vic2V0KHN1YnNldF9zaWduaWZfaGlnaExELCBzdWJzZXRfc2lnbmlmX2hpZ2hMRCRjb25kaXRpb24gJWluJSBjKCJMRCIsICJDTF9PMiIpICYgc3Vic2V0X3NpZ25pZl9oaWdoTEQkdGltZT09MC4wKQoKZ2V0X2NvbnRyb2xzIDwtIGZ1bmN0aW9uKGNvbmRfc3BlYywgc2dSTkFfc3BlYyl7CiAgY29udHJvbF90YWJsZSA8LSBzdWJzZXRfc2lnbmlmX2hpZ2hMRFtzdWJzZXRfc2lnbmlmX2hpZ2hMRCRjb25kaXRpb24gIT0gdW5pcXVlKGNvbmRfc3BlYykgJiBzdWJzZXRfc2lnbmlmX2hpZ2hMRCRzZ1JOQV90YXJnZXQgPT0gdW5pcXVlKHNnUk5BX3NwZWMpICYgc3Vic2V0X3NpZ25pZl9oaWdoTEQkdGltZSA9PSAwLF0KICBjb250cm9sX3RhYmxlJGZpdG5lc3MKfQoKc3Vic2V0X3NpZ25pZl9oaWdoTEQgPC0gZHBseXI6OmxlZnRfam9pbigKICBzdWJzZXRfc2lnbmlmX2hpZ2hMRCwKICBzdWJzZXRfc2lnbmlmX2hpZ2hMRCAlPiUKICAgIGRwbHlyOjpncm91cF9ieShzZ1JOQV90YXJnZXQsIGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKAogICAgICAuZ3JvdXBzID0gImtlZXAiLAogICAgICAjIGFwcGx5IFdpbGNveG9uIHJhbmsgc3VtIHRlc3QgYWdhaW5zdCBvdGhlciBjb25kaXRpb24KICAgICAgcF9maXRuZXNzX2NvbmRpdGlvbiA9IHN0YXRzOjp3aWxjb3gudGVzdCgKICAgICAgICB4ID0gZml0bmVzcywKICAgICAgICB5ID0gZ2V0X2NvbnRyb2xzKGNvbmRpdGlvbiwgc2dSTkFfdGFyZ2V0KSwKICAgICAgICBwYWlyZWQgPSBUUlVFLAogICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIKICAgICAgICApJHAudmFsdWUKICAgICAgKSwKICBieSA9IGMoInNnUk5BX3RhcmdldCIsICJjb25kaXRpb24iLCAidGltZSIpCiAgKSAKCnN1YnNldF9zaWduaWZfaGlnaExEIDwtIHN1YnNldF9zaWduaWZfaGlnaExEICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgbXV0YXRlKAogICAgcF9maXRuZXNzX2NvbmRpdGlvbl9hZGogPSBzdGF0czo6cC5hZGp1c3QocF9maXRuZXNzX2NvbmRpdGlvbiwgbWV0aG9kID0gIkJIIikKICAgICkKYGBgCgpgYGB7ciBmaXRuZXNzLWZpdG5lc3MtcGxvdC1XaWxjb3hvbi1zaWduaWZpY2FudC1MRH0Kc3Vic2V0X3NpZ25pZl9oaWdoTERfcmVkIDwtIHVuaXF1ZShzdWJzZXRfc2lnbmlmX2hpZ2hMRFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIsICJwX2ZpdG5lc3NfY29uZGl0aW9uX2FkaiIpXSkKc3Vic2V0X3NpZ25pZl9oaWdoTERfcmVkIDwtIHBpdm90X3dpZGVyKHN1YnNldF9zaWduaWZfaGlnaExEX3JlZCwgdmFsdWVzX2Zyb20gPWMobm9ybSxwX2ZpdG5lc3NfY29uZGl0aW9uX2FkaiksIG5hbWVzX2Zyb209Y29uZGl0aW9uKQoKIyBsYWJlbHMsIGFscGhhIGFuZCBzaXplIGFjY29yZGluZyB0byBzaWduaWZpY2FuY2UKd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZiA8LSAiTk8iCndpZGVfZml0bmVzc19MRF9PTCRzaWduaWZbd2lkZV9maXRuZXNzX0xEX09MJHNnUk5BX3RhcmdldCAlaW4lIHVuaXF1ZShzdWJzZXQoc3Vic2V0X3NpZ25pZl9oaWdoTEQsIHN1YnNldF9zaWduaWZfaGlnaExEJHBfZml0bmVzc19jb25kaXRpb25fYWRqIDwgMC40KSkkc2dSTkFfdGFyZ2V0XSA8LSAiU0lHIgp3aWRlX2ZpdG5lc3NfTERfT0wkZGVsYWJlbCA8LSBOQQp3aWRlX2ZpdG5lc3NfTERfT0wkZGVsYWJlbFt3aWRlX2ZpdG5lc3NfTERfT0wkc2lnbmlmID09IlNJRyJdIDwtIGFzLmNoYXJhY3Rlcih3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0W3dpZGVfZml0bmVzc19MRF9PTCRzaWduaWYgPT0iU0lHIl0pCndpZGVfZml0bmVzc19DTF9PTFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kc2lnbmlmIDwtICJXVCIgIyB0byBlbnN1cmUgV1QgaXMgdmlzaWJsZQoKcCA8LSBnZ3Bsb3Qod2lkZV9maXRuZXNzX0xEX09MLCBhZXMoeD1ub3JtX0NMX08yLCB5PW5vcm1fTEQsIGNvbG9yPWRpZmYsIGxhYmVsPWRlbGFiZWwsIHNoYXBlPVdUX25vdF9XVCkpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPXNpZ25pZiwgYWxwaGE9c2lnbmlmKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPXNpZ25pZl9zaXplKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9c2lnbmlmX2FscGhhKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHk9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBhdCBjb250aW51b3VzIGxpZ2h0LCA1JSBDTzIsIDk1JSBOMiwgMCUgTzIiLCB4PSJXZWlnaHRlZCBtZWFuIGZpdG5lc3MgdmFsdWUgYXQgY29udGludW91cyBsaWdodCwgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1sxXSxzbG9wZT1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0iYmxhY2siKSArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gZG90cGxvdF9jb2xvcnMpICsgZ2VvbV90ZXh0X3JlcGVsKCkgIysgeGxpbSgwLjc1LCAyLjUpICt5bGltKDAuNzUsMi4yNSkKcApnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi93Zml0bmVzc19MRF9DTE8yX1dpbGNveF9zaWduaWYucGRmIiwgcGxvdD1wLCB3aWR0aD0xMi41LCBoZWlnaHQ9MTIuNSwgdW5pdHM9ImNtIikKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvd2ZpdG5lc3NfTERfQ0xPMl9XaWxjb3hfc2lnbmlmLnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTIuNSwgaGVpZ2h0PTEyLjUsIHVuaXRzPSJjbSIpCgp3aWRlX2ZpdG5lc3NfQ0xfT0xbd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZj09IlNJRyIsXVtvcmRlcih3aWRlX2ZpdG5lc3NfTERfT0xbd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZj09IlNJRyIsXSRub3JtX0xELCBkZWNyZWFzaW5nPVRSVUUpLF0KYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KIyMgY29sb3JpbmcgYWNjb3JkaW5nIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCndpZGVfZml0bmVzc19MRF9PTCRkaWZmIDwtICJOTyIKd2lkZV9maXRuZXNzX0xEX09MJGRpZmZbd2lkZV9maXRuZXNzX0xEX09MJGRpc3RhbmNlX2xtID4gQ0xfTERfY3V0b2ZmXSA8LSAiTEQiICMgaWYgdXNpbmcgY3V0LW9mZgp3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPCAoLUNMX0xEX2N1dG9mZildIDwtICJDTF9PMiIgIyBpZiB1c2luZyBjdXQtb2ZmCndpZGVfZml0bmVzc19MRF9PTFt3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kZGlmZiA8LSAiV1QiCgojIGxhYmVscywgYWxwaGEgYW5kIHNpemUgYWNjb3JkaW5nIHRvIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgdG8gV1QgdmFyaWFudAp3aWRlX2ZpdG5lc3NfTERfT0wkc2lnbmlmIDwtICJOTyIKd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZlt3aWRlX2ZpdG5lc3NfTERfT0wkcF9maXRfYWRqX1dUX0NMX08yIDwgYWRqcF9jdXRvZmYgJiB3aWRlX2ZpdG5lc3NfTERfT0wkcF9maXRfYWRqX1dUX0xEIDwgYWRqcF9jdXRvZmYgJiAod2lkZV9maXRuZXNzX0xEX09MJGRpZmY9PSJMRCIgfCB3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZj09IkNMX08yIikgJiB3aWRlX2ZpdG5lc3NfTERfT0wkbnVtX2JhcmNvZGVzID49IGJhcmNvZGVfY3V0b2ZmXSA8LSAiU0lHIgpzaWduaWZfc2l6ZSA8LSBjKCJOTyI9MC4yLCAiU0lHIj0wLjMpCnNpZ25pZl9hbHBoYSA8LSBjKCJOTyI9MC4yLCAiU0lHIj0wLjgpCndpZGVfZml0bmVzc19MRF9PTCRkZWxhYmVsIDwtIGFzLmNoYXJhY3Rlcih3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0KQp3aWRlX2ZpdG5lc3NfTERfT0wkZGVsYWJlbFt3aWRlX2ZpdG5lc3NfTERfT0wkc2lnbmlmICE9IlNJRyJdIDwtIE5BCgpwIDwtIGdncGxvdCh3aWRlX2ZpdG5lc3NfTERfT0wsIGFlcyh4PW5vcm1fQ0xfTzIsIHk9bm9ybV9MRCwgY29sb3I9ZGlmZiwgc2hhcGU9V1Rfbm90X1dULCBsYWJlbD1kZWxhYmVsKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPXNpZ25pZiwgYWxwaGE9c2lnbmlmKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9c2lnbmlmX3NpemUpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1zaWduaWZfYWxwaGEpICsgdGhlbWVfbGlnaHQoKSArIGxhYnMoeT0iV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIHZhbHVlIGluIGxpZ2h0LWRhcmsgY3ljbGVzLCA1JSBDTzIsIDc1JSBOMiwgMjAlIE8yIiwgeD0iV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIHZhbHVlIGluIGNvbnRpbnVvdXMgbGlnaHQsIDUlIENPMiwgNzUlIE4yLCAyMCUgTzIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0sc2xvcGU9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMl0sbGluZXR5cGU9ImRhc2hlZCIsY29sb3I9ImJsYWNrIikrIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gZG90cGxvdF9jb2xvcnMpICArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1sxXS1DTF9MRF9jdXRvZmYsIHNsb3BlPWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzJdLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9ImJsYWNrIikgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0rQ0xfTERfY3V0b2ZmLCBzbG9wZT1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yPSJibGFjayIpICsgZ2VvbV90ZXh0X3JlcGVsKCkgIyAgKyB4bGltKC02LCArNykgK3lsaW0oLTYsKzcpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3dmaXRuZXNzX0NMTzJfTERfYXR0ZW1wdF9maW5kRGlmZmVyZW50VmFyaWFudHMucGRmIiwgcGxvdD1wLCB3aWR0aD0xMi41LCBoZWlnaHQ9MTIuNSwgdW5pdHM9ImNtIikKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvd2ZpdG5lc3NfQ0xPMl9MRF9hdHRlbXB0X2ZpbmREaWZmZXJlbnRWYXJpYW50cy5wbmciLCBwbG90PXAsIHdpZHRoPTEyLjUsIGhlaWdodD0xMi41LCB1bml0cz0iY20iKQpwCgp3aWRlX2ZpdG5lc3NfTERfT0xfc2lnIDwtIHN1YnNldCh3aWRlX2ZpdG5lc3NfTERfT0wsIHdpZGVfZml0bmVzc19MRF9PTCRzaWduaWY9PSJTSUciKQp3aWRlX2ZpdG5lc3NfTERfT0xfc2lnW29yZGVyKHdpZGVfZml0bmVzc19MRF9PTF9zaWckZGlzdGFuY2VfbG0sIGRlY3JlYXNpbmcgPSBUUlVFKSxdWzE6MTAsXQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpwb3NzaWJseV9oaWdoZXJfaW5MRCA8LSBjKCJLNDU2SCIsICJHNDMyUyIsICJLOTlOIiwgIkgxMjZHIiwiV1QiLCBuZWdfY29udHJvbF9LMjE0KQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJLNDU2SCI9IiMzMzIyODhmZiIsICJHNDMyUyI9IiMxMTc3MzNmZiIsICJIMzU4TSI9IiM0NGFhOTlmZiIsICJIMTI2TiI9IiM4OGNjZWVmZiIsICJIMTI2RyI9IiNkZGNjNzdmZiIsICJLOTlOIj0iI2NjNjY3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnNpbmdsZV9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIHBvc3NpYmx5X2hpZ2hlcl9pbkxEKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoc2luZ2xlX3N1YnNldCkgKyBsYWJzKHRpdGxlPSJtb3N0IGRpZmZlcmVudCB2YXJpYW50cyIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL21vc3REaWZmZXJlbnRfTERfQ0xPMl90aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wLCB3aWR0aD0xMCwgaGVpZ2h0PTEwKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvbW9zdERpZmZlcmVudF9MRF9DTE8yX3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXAsIHdpZHRoPTEwLCBoZWlnaHQ9MTApCgp1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIHBvc3NpYmx5X2hpZ2hlcl9pbkxEKVssYygic2dSTkFfdGFyZ2V0IiwgImNvbmRpdGlvbiIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pCmBgYAoKIyBHZW5lcmFsbHkgaW50ZXJlc3RpbmcgbXV0YW50cwoKSW4gdGhlIGZvbGxvd2luZywgYW4gYXR0ZW1wdCBpcyBtYWRlIHRvIHBpbnBvaW50IHNvbWUgaW50ZXJlc3RpbmcgdmFyaWFudHMgdGhhdCBhcmUgd29ydGggdGVzdGluZy4gVGhlIGZvbGxvd2luZyBjaHVuayBvZiBjb2RlIHNlbGVjdHMgYWxsIHZhcmlhbnRzIHdoaWNoIGFyZSBwZXJmb3JtaW5nIHdlbGwgdW5kZXIgYWxsIGNvbmRpdGlvbnMgYW5kIGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCB0byB0aGUgYmFzZSB2YXJpYW50LiBGdXJ0aGVybW9yZSwgb25seSB2YXJpYW50cyB3aXRoIGF0IGxlYXN0IHR3byBiYXJjb2RlcyBhcmUgdGFrZW4gaW50byBhY2NvdW50LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CmNvbHVtbnMgPC0gYygic2dSTkFfdGFyZ2V0IiwgICJjb25kaXRpb24iLCAibm9ybSIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIiwgIm51bWJlcl9tdXRzIikKZml0bmVzc19kYXRhX2dvb2RWYXJpYW50cyA8LSBwaXZvdF93aWRlcih1bmlxdWUoZml0bmVzc19kYXRhWyxjb2x1bW5zXSksIHZhbHVlc19mcm9tID0gYygibm9ybSIsICJwX2ZpdF9hZGpfV1QiKSwgbmFtZXNfZnJvbSA9IGNvbmRpdGlvbikKZml0bmVzc19kYXRhX2dvb2RWYXJpYW50cyA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMsIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkcF9maXRfYWRqX1dUX0NMX04yIDwgMC4wNSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkcF9maXRfYWRqX1dUX0NMX08yIDwgMC4wNSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkcF9maXRfYWRqX1dUX0xEIDwgMC4wNSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9DTF9OMiA+IDEgJiBmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJG5vcm1fQ0xfTzIgPiAxICYgZml0bmVzc19kYXRhX2dvb2RWYXJpYW50cyRub3JtX0xEID4gMSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbnVtX2JhcmNvZGVzID4gMSkpCgpsZW5ndGgodW5pcXVlKGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkc2dSTkFfdGFyZ2V0KSkKbGVuZ3RoKHVuaXF1ZShmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJHNnUk5BX3RhcmdldCkpL2xlbmd0aCh1bmlxdWUoZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCkpCgpmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJGNvbWJpbmVkX25vcm1zIDwtIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9DTF9OMiAqIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9DTF9PMiAqIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9MRApwcmludChmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzW29yZGVyKGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkY29tYmluZWRfbm9ybXMsIGRlY3JlYXNpbmcgPSBUUlVFKSxdKQp3cml0ZV9jc3YoZml0bmVzc19kYXRhX2dvb2RWYXJpYW50c1tvcmRlcihmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJGNvbWJpbmVkX25vcm1zLCBkZWNyZWFzaW5nID0gVFJVRSksXSwgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9nb29kVmFyaWFudHMuY3N2IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KaGlnaFNjb3Jpbmdfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJIMTI2TiIsICJIMTI2TSIsICJXMTI4SSIsICJLMzYwSSIsICJLMzYwTSIsICJRMTQyRCIsICJLMjMzQyIsICJIMzU4QyIsICJLMzYwTCIsICJIMzU4TCIsICJLOTlOIiwgIkszNjBDIiwgIlkzMzNWIiwgIkgxMjZHIiwgIk0xNTRFLEsyNjFBLFE0MDZELFM0NDZUIiwgIlExNDJFIiwgIk0zNDVRIiwgIkYxMDRWIiwgIlY5OFEiLCAiSzI2MUEsSDI2NUEsUTQwNkQsUzQ0NkkiLCAiSzk5VCIsICJLMjMzTCIsICJNMzQ1ViIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoaGlnaFNjb3Jpbmdfc3Vic2V0KSArIGxhYnModGl0bGU9IkZpdHRlc3QgdmFyaWFudHMgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9oaWdoZXN0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9oaWdoZXN0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCmBgYAoKUGFydGlhbGx5LCBleGNoYW5nZXMgYXQgdGhlIHNhbWUgYW1pbm8gYWNpZCBwb3NpdGlvbiBzY29yZSBxdWl0ZSBzaW1pbGFybHkuIFdlIGNhbm5vdCBkaXN0aW5ndWlzaCB0aGVpciBmaXRuZXNzIG9uIGJhc2lzIG9mIG91ciBkYXRhLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJIMTI2TiIsICJIMTI2TSIsICJIMTI2RyIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iVmVyeSBmaXQgSDEyNiB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL0gxMjZfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9IMTI2X3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkszNjBJIiwgIkszNjBNIiwgIkszNjBMIiwgIkszNjBDIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJWZXJ5IGZpdCBLMzYwIHZhcmlhbnRzIHdpdGggOTUlIENJIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvSzM2MF9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL0szNjBfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSDM1OEMiLCAiSDM1OEwiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZlcnkgZml0IEgzNTggdmFyaWFudHMgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9IMzU4X3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvSDM1OF9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJRMTQyRCIsICJRMTQyRSIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iVmVyeSBmaXQgUTE0MiB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL1ExNDJfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9RMTQyX3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIk0zNDVRIiwgIk0zNDVWIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJWZXJ5IGZpdCBNMzQ1IHZhcmlhbnRzIHdpdGggOTUlIENJIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvTTM0NV9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL00zNDVfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzIzM0MiLCAiSzIzM0wiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZlcnkgZml0IEsyMzMgdmFyaWFudHMgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9LMjMzX3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvSzIzM19zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJLOTlOIiwgIks5OVQiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZlcnkgZml0IEs5OSB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL0s5OV9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL0s5OV9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJXMTI4SSIsICJZMzMzViIsICJGMTA0ViIsICJWOThRIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlcxMjhJIj0iIzMzMjI4OGZmIiwgIlkzMzNWIj0iIzExNzczM2ZmIiwgIlY5OFEiPSIjODhjY2VlZmYiLCAiRjEwNFYiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikgI2RpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlcxMjhJIj0iIzMzMjI4OGZmIiwgIlkzMzNWIj0iIzExNzczM2ZmIiwgIkYxMDRWIj0iIzQ0YWE5OWZmIiwgIlY5OFEiPSIjODhjY2VlZmYiLCAiRjEwNFYiPSIjZGRjYzc3ZmYiLCAiSzk5TiI9IiNjYzY2NzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iVmVyeSBmaXQgdmFyaWFudHMgd2l0aCA5NSUgQ0kiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9yZW1haW5pbmdfaGlnaFNjb3JpbmdfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3JlbWFpbmluZ19oaWdoU2NvcmluZ192YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQpgYGAKCiMgUGxvdCBlcGlzdGF0aWMgZWZmZWN0cwoKIyMgR2VuZXJhbCBvdmVydmlldyBvdmVyIGVwaXN0YXNpcyBpbiBkYXRhIHNldAoKRnJvbSBNaXRvbiBldCBhbC4sIDIwMTYgKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Z1bGwvMTAuMTAwMi9wcm8uMjg3Nik6IAoiV2UgY2FsY3VsYXRlZCB0aGUgZm9sZCBjaGFuZ2UgaW4gZW56eW1lIGZpdG5lc3MgKEYpIHByb3ZpZGVkIGJ5IGEgbXV0YXRpb24gaSBvbiB0aGUgd2lsZC10eXBlIGJhY2tncm91bmQgKM6URnd0LGk9Rnd0K2kvRnd0KSBhbmQgdGhlIGNoYW5nZSBjYXVzZWQgYnkgdGhlIHNhbWUgbXV0YXRpb24gb24gdGhlIGludGVybWVkaWF0ZSB2YXJpYW50IGogaW4gdGhlIHRyYWplY3RvcnksICjOlEZqLGk9RmoraS9GaikgWy4uLl0gZXBpc3Rhc2lzIHdhcyBkZXRlcm1pbmVkIGJ5IGNvbXBhcmluZyB0aGUgZm9sZCBjaGFuZ2VzIGluIHRoZSB0cmFqZWN0b3J5IG92ZXIgdGhlIHdpbGQtdHlwZSBiYWNrZ3JvdW5kICjOlEZqLGkvzpRGd3QsaSkuIEZvciB0aGUgc2FrZSBvZiBzaW1wbGljaXR5LCB3ZSBjb25zaWRlcmVkIHRoYXQg4omlMS41LWZvbGQgY2hhbmdlIGlzIHNpZ25pZmljYW50LCBhbmQgbGVzcyB0aGFuIDEuNS1mb2xkIGNoYW5nZSBpcyBuZXV0cmFsIChvciBuZWFybHkgbmV1dHJhbCkuIFsuLi5dIE5ldXRyYWwgZGVzaWduYXRlcyBtdXRhdGlvbnMgdGhhdCBzaG93IGxlc3MgdGhhbiAxLjUtZm9sZCBjaGFuZ2UgaW4gZW56eW1lIGZpdG5lc3Mgb24gYm90aCBiYWNrZ3JvdW5kcyAoMC43IDwgW86URmosaSBhbmQgzpRGd3QsaV0gPCAxLjUpLiAoaWkgLSB2KSBGdW5jdGlvbmFsIHJlZmVycyB0byBub24tbmV1dHJhbCBtdXRhdGlvbnMgdGhhdCBleGhpYml0ID4xLjUtZm9sZCBpbXByb3ZlbWVudCBpbiBlaXRoZXIgZ2VuZXRpYyBiYWNrZ3JvdW5kICjOlEZqLGkgb3IgzpRGd3QsaSA+IDEuNSkuIEFtb25nIGZ1bmN0aW9uYWwgbXV0YXRpb25zLCBpaSkgbm8gZXBpc3Rhc2lzIHJlZmVycyB0byBtdXRhdGlvbnMgdGhhdCBkbyBub3Qgc2lnbmlmaWNhbnRseSBhbHRlciB0aGUgZW56eW1lIGZpdG5lc3MgZGVwZW5kaW5nIG9uIHRoZSBiYWNrZ3JvdW5kIChtdXRhdGlvbnMgYXJlIGFkZGl0aXZlLCDOlEZqLGkvzpRGd3QsaSDiiLwgMSkuIChpaWkg4oCTIGl2KSBQb3NpdGl2ZSBlcGlzdGFzaXMgYXBwbGllcyB0byBhIG11dGF0aW9uIHRoYXQgYmVjb21lcyBtb3JlIGJlbmVmaWNpYWwgd2hlbiBjb21iaW5lZCB3aXRoIHByaW9yIHN1YnN0aXR1dGlvbnMgb24gdGhlIHRyYWplY3RvcnksIGNvbXBhcmVkIHRvIGl0cyBlZmZlY3Qgb24gdGhlIHdpbGQtdHlwZSBiYWNrZ3JvdW5kICjOlEZqLGkvzpRGd3QsaSA+IDEuNSkuIEl0IGNhbiBiZSBkaXZpZGVkIGluIHR3byBzdWJjbGFzc2VzOiBpaWkpIFBvc2l0aXZlIG1hZ25pdHVkZSBlcGlzdGFzaXMgcmVmZXJzIHRvIGNhc2VzIHdoZXJlIHRoZSBlZmZlY3QgaXMgbmV1dHJhbCBvciBwb3NpdGl2ZSBvbiB0aGUgd2lsZC10eXBlIGJhY2tncm91bmQgYW5kIGlzIGZ1cnRoZXIgYW1wbGlmaWVkIG9uIHRoZSB0cmFqZWN0b3J5ICjOlEZqLGkg4omrIM6URnd0LOKAiWnigIk+4oCJMC43IG9yIM6URmos4oCJaSDiiasgzpRGd3Qs4oCJaeKAiT7igIkxLjUpOyBhbmQgaXYpIFBvc2l0aXZlIHNpZ24gZXBpc3Rhc2lzLCB3aGljaCByZWZlcnMgdG8gbXV0YXRpb25zIGNhdXNpbmcgYSBkZWxldGVyaW91cyBlZmZlY3Qgb24gdGhlIHdpbGQtdHlwZSBiYWNrZ3JvdW5kIGJ1dCBhIGJlbmVmaWNpYWwgZWZmZWN0IG9uIHRoZSB0cmFqZWN0b3J5ICjOlEZ3dCzigIlpIDwwLjcgYW5kIM6URmos4oCJaeKAiT7igIkxLjUpLiB2KSBOZWdhdGl2ZSBlcGlzdGFzaXMgYXBwbGllcyB0byBhIG11dGF0aW9uIHRoYXQgYmVjb21lcyBsZXNzIGJlbmVmaWNpYWwgb24gdGhlIHRyYWplY3RvcnkgYmFja2dyb3VuZCBjb21wYXJlZCB0byBpdHMgb3JpZ2luYWwgZWZmZWN0IG9uIHRoZSB3aWxkIHR5cGUgKM6URmos4oCJaS/OlEZ3dCzigIlpIDwwLjcpLiIKCmBgYHtyIGxvYWQtZXBpc3Rhc2lzLWRhdGFzZXRzLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQplcGlzdGF0aWNfdGFibGUgPC0gcmVhZF90c3YoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9lcGlzdGFzaXMvdGFibGVfdmFsdWVzLnRzdiIpCmVwaXN0YXRpY190YWJsZSRyYXRpb19XVF9oaWdoZXJPcmRlciA8LSBlcGlzdGF0aWNfdGFibGUkVmFyaWFudF9yYXRpby9lcGlzdGF0aWNfdGFibGUkRXhjaGFuZ2VfdmFsdWVfYW5kX3JhdGlvX2luX1dUCmBgYAoKSWRlbnRpZnkgY3V0LW9mZiB2YWx1ZXMgZm9yIGdvb2QgLyBiYWQgdmFyaWFudHMgLSB1c2UgYWRqLiBwIHZhbHVlcyA8IDAuMDUgdG8gZG8gc28sIHF1ZXN0aW9uICJjYW4gSSBzaWduaWZpY2FudGx5IGRpc3Rpbmd1aXNoIHRoaXMgdmFyaWFudCBmcm9tIHRoZSBiYXNlIHZhcmlhbnQ/IgpTZWVtcyBhcyBpZiBkaWZmZXJlbmNlcyBhcyBzbWFsbCBhcyBhIG5vcm1lZCB2YWx1ZSBvZiAxLjEgYXJlIG9mdGVuIGFscmVhZHkgc2lnbmlmaWNhbnQgY29tcGFyZWQgdG8gdGhlIGJhc2UgdmFyaWFudCAtIEkgYXNzdW1lIGEgbm9ybWVkIHZhbHVlIG9mIDEuMjUgaXMgYWJvdXQgcmlnaHQuCgpgYGB7cn0KY29tYmluYXRvcmlhbF93aWRlIDwtIHN1YnNldChjb21iaW5hdG9yaWFsX3dpZGUsIGNvbWJpbmF0b3JpYWxfd2lkZSRwX2ZpdF9hZGpfV1Q8MC4wNSkgIyBpbiBjYXNlIHRoaW5ncyB3ZXJlIGNoYW5nZWQgYWJvdmUKYSA8LSB1bmlxdWUoc3Vic2V0KGNvbWJpbmF0b3JpYWxfd2lkZSwgY29tYmluYXRvcmlhbF93aWRlJG5vcm0gPiAxLjApJG5vcm0pCmEgPC0gYVtvcmRlcihhKV0KYVsxOjIwXQpgYGAKCkFsc28sIGRpZmZlcmVuY2VzIGFzIHNtYWxsIGFzIDAuOSBjb21wYXJlZCB0byAxLjAgY2FuIGJlIGRpc3Rpbmd1aXNoZWQgLSBzbyBJIGd1ZXNzIGEgZ29vZCBjdXQtb2ZmIG1pZ2h0IGJlIDAuOC4gCgpgYGB7cn0KYSA8LSB1bmlxdWUoc3Vic2V0KGNvbWJpbmF0b3JpYWxfd2lkZSwgY29tYmluYXRvcmlhbF93aWRlJG5vcm0gPCAxLjApJG5vcm0pCmEgPC0gYVtvcmRlcihhLCBkZWNyZWFzaW5nPVRSVUUpXQphWzE6MjBdCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9kYXRhZnJhbWUgPC0gdW5pcXVlKGRwbHlyOjpzdW1tYXJpemUoLmRhdGE9ZXBpc3RhdGljX3RhYmxlLAogICAgICAuYnkgPSBjKEV4Y2hhbmdlLCBDb25kaXRpb24pLAogICAgIyB2YWx1ZSBpbiBXVCBiYWNrZ3JvdW5kCiAgICB2YWx1ZV9iYWNrZ3JvdW5kID0gdW5pcXVlKC5kYXRhW1siRXhjaGFuZ2VfdmFsdWVfYW5kX3JhdGlvX2luX1dUIl1dKSwKICAgICMgbnVtYmVyIG5lZy4gZWZmZWN0IGluIGhpZ2hlciBvcmRlcgogICAgbnVtYmVyX25lZ2F0aXZlX2V4Y2hhbmdlcyA9IHN1bSguZGF0YVtbIlZhcmlhbnRfcmF0aW8iXV0gPCAwLjgpLCAjIGFkanVzdGVkIHRvIDAuOCBhY2NvcmRpbmcgdG8gb2JzZXJ2YXRpb25zIGRldGFpbGVkIGFib3ZlIGZyb20gb3JpZ2luYWxseSAwLjcgaW4gcGFwZXIKICAgICMgbnVtYmVyIG5ldXRyYWwgZWZmZWN0IGluIGhpZ2hlciBvcmRlciAKICAgIG51bWJlcl9uZXV0cmFsX2V4Y2hhbmdlcyA9IHN1bSguZGF0YVtbIlZhcmlhbnRfcmF0aW8iXV0gPiAwLjggJiAuZGF0YVtbIlZhcmlhbnRfcmF0aW8iXV0gPCAxLjI1KSwgIyBhZGp1c3RlZCB0byAwLjggYW5kIDEuMiBmcm9tIDAuNyBhbmQgMS41CiAgICAjIG51bWJlciBwb3MuIGVmZmVjdCBpbiBoaWdoZXIgb3JkZXIKICAgIG51bWJlcl9wb3NpdGl2ZV9leGNoYW5nZXMgPSBzdW0oLmRhdGFbWyJWYXJpYW50X3JhdGlvIl1dID4gMS4yNSksICMgYWRqdXN0ZWQgZnJvbSAxLjUgaW4gcGFwZXIgdG8gMS4yIGFjY29yZGluZyB0byBvYnNlcnZhdGlvbnMgZGV0YWlsZWQgYWJvdmUKICAgICMgbm8gZXBpc3Rhc2lzICgwLjcgPCB2YWx1ZSA8IDEuNSkKICAgIG51bWJlcl9ub19lcGlzdGFzaXMgPSBzdW0oLmRhdGFbWyJyYXRpb19XVF9oaWdoZXJPcmRlciJdXSA+IDAuNyAmIC5kYXRhW1sicmF0aW9fV1RfaGlnaGVyT3JkZXIiXV0gPCAxLjUpLAogICAgIyBwb3NpdGl2ZSBlcGlzdGFzaXMgKD4xLjUpCiAgICBudW1iZXJfcG9zaXRpdmVfZXBpc3Rhc2lzID0gc3VtKC5kYXRhW1sicmF0aW9fV1RfaGlnaGVyT3JkZXIiXV0gPiAxLjUpLAogICAgIyBuZWdhdGl2ZSBlcGlzdGFzaXMgKDwwLjcpCiAgICBudW1iZXJfbmVnYXRpdmVfZXBpc3Rhc2lzID0gc3VtKC5kYXRhW1sicmF0aW9fV1RfaGlnaGVyT3JkZXIiXV0gPCAwLjcpLAogICAgIyB0b3RhbCBoaWdoZXIgZXhjaGFuZ2UKICAgIG51bWJlcl9oaWdoZXJfZXhjaGFuZ2VzID0gbigpCiAgICApKQpgYGAKCmBgYHtyfQpleGNoYW5nZXNfZGF0YXNldCA8LSBzdW1tYXJpemVfZGF0YWZyYW1lWyxjKCJFeGNoYW5nZSIsICJDb25kaXRpb24iLCAibnVtYmVyX25lZ2F0aXZlX2V4Y2hhbmdlcyIsICJudW1iZXJfbmV1dHJhbF9leGNoYW5nZXMiLCAibnVtYmVyX3Bvc2l0aXZlX2V4Y2hhbmdlcyIpXQpleGNoYW5nZXNfZGF0YXNldCA8LSBwaXZvdF9sb25nZXIoZXhjaGFuZ2VzX2RhdGFzZXQsICFjKEV4Y2hhbmdlLCBDb25kaXRpb24pLCB2YWx1ZXNfdG89Im51bWJlciIsIG5hbWVzX3RvPSJleGNoYW5nZXMiKQoKZXhjaGFuZ2VzX2RhdGFzZXQkRXhjaGFuZ2UgPC0gZmFjdG9yKGV4Y2hhbmdlc19kYXRhc2V0JEV4Y2hhbmdlLCBsZXZlbHM9cmV2KGMoIkgxNDFMIiwgIkgxNDFWIiwgIk0xNTREIiwgIk0xNTRFIiwgIk0xNTRLIiwgIk0xNTRTIiwgIksyNjFBIiwgIksyNjFEIiwgIksyNjFFIiwgIksyNjFGIiwgIkgyNjVBIiwgIkgyNjVFIiwgIkgyNjVLIiwgIkgyNjVSIiwgIlYzMzdBIiwgIlYzMzdTIiwgIlE0MDZEIiwgIlE0MDZFIiwgIlM0NDZBIiwgIlM0NDZJIiwgIlM0NDZUIikpKQoKY29sb3JzX2V4Y2hhbmdlcyA8LSBicmV3ZXIucGFsKG4gPSA5LCBuYW1lID0gIkJsdWVzIilbYygzLDYsOSldCm5hbWVzKGNvbG9yc19leGNoYW5nZXMpIDwtIGMoIm51bWJlcl9wb3NpdGl2ZV9leGNoYW5nZXMiLCAibnVtYmVyX25ldXRyYWxfZXhjaGFuZ2VzIiwgIm51bWJlcl9uZWdhdGl2ZV9leGNoYW5nZXMiKQoKcCA8LSBnZ3Bsb3QoZXhjaGFuZ2VzX2RhdGFzZXQpICsKICBnZW9tX2JhcihhZXMoeCA9IG51bWJlciwgeSA9IEV4Y2hhbmdlLCBmaWxsID0gZXhjaGFuZ2VzKSwKICAgICAgICAgICBwb3NpdGlvbiA9ICJmaWxsIiwKICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IikgICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yc19leGNoYW5nZXMpICsKICBmYWNldF9ncmlkKH4gQ29uZGl0aW9uLCBzd2l0Y2ggPSAieCIpICsgCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lKHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIikpCnAgCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL2VwaXN0YXNpcy9Qb3NpdGl2ZV9uZWdhdGl2ZUV4Y2hhbmdlc19oaWdoZXJPcmRlcnZhcmlhbnRzLnBkZiIsIHBsb3Q9cCwgd2lkdGg9Ny41LCBoZWlnaHQ9MykKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvZXBpc3Rhc2lzL1Bvc2l0aXZlX25lZ2F0aXZlRXhjaGFuZ2VzX2hpZ2hlck9yZGVydmFyaWFudHMucG5nIiwgcGxvdD1wLCB3aWR0aD03LjUsIGhlaWdodD0zKQpgYGAKCmBgYHtyfQplcGlzdGFzaXNfZGF0YXNldCA8LSBzdW1tYXJpemVfZGF0YWZyYW1lWyxjKCJFeGNoYW5nZSIsICJDb25kaXRpb24iLCAibnVtYmVyX25vX2VwaXN0YXNpcyIsICJudW1iZXJfcG9zaXRpdmVfZXBpc3Rhc2lzIiwgIm51bWJlcl9uZWdhdGl2ZV9lcGlzdGFzaXMiKV0KZXBpc3Rhc2lzX2RhdGFzZXQgPC0gcGl2b3RfbG9uZ2VyKGVwaXN0YXNpc19kYXRhc2V0LCAhYyhFeGNoYW5nZSwgQ29uZGl0aW9uKSwgdmFsdWVzX3RvPSJudW1iZXIiLCBuYW1lc190bz0iZXBpc3Rhc2lzIikKCmVwaXN0YXNpc19kYXRhc2V0JEV4Y2hhbmdlIDwtIGZhY3RvcihlcGlzdGFzaXNfZGF0YXNldCRFeGNoYW5nZSwgbGV2ZWxzPXJldihjKCJIMTQxTCIsICJIMTQxViIsICJNMTU0RCIsICJNMTU0RSIsICJNMTU0SyIsICJNMTU0UyIsICJLMjYxQSIsICJLMjYxRCIsICJLMjYxRSIsICJLMjYxRiIsICJIMjY1QSIsICJIMjY1RSIsICJIMjY1SyIsICJIMjY1UiIsICJWMzM3QSIsICJWMzM3UyIsICJRNDA2RCIsICJRNDA2RSIsICJTNDQ2QSIsICJTNDQ2SSIsICJTNDQ2VCIpKSkKCmNvbG9yc19lcGlzdGFzaXMgPC0gYnJld2VyLnBhbChuID0gOSwgbmFtZSA9ICJCbHVlcyIpW2MoMyw2LDkpXQpuYW1lcyhjb2xvcnNfZXBpc3Rhc2lzKSA8LSBjKCJudW1iZXJfcG9zaXRpdmVfZXBpc3Rhc2lzIiwgIm51bWJlcl9ub19lcGlzdGFzaXMiLCAibnVtYmVyX25lZ2F0aXZlX2VwaXN0YXNpcyIpCgpwIDwtIGdncGxvdChlcGlzdGFzaXNfZGF0YXNldCkgKwogIGdlb21fYmFyKGFlcyh4ID0gbnVtYmVyLCB5ID0gRXhjaGFuZ2UsIGZpbGwgPSBlcGlzdGFzaXMpLAogICAgICAgICAgIHBvc2l0aW9uID0gImZpbGwiLAogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yc19lcGlzdGFzaXMpICsKICBmYWNldF9ncmlkKH4gQ29uZGl0aW9uLCBzd2l0Y2ggPSAieCIpICsgCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lKHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIikpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvZXBpc3Rhc2lzL1Bvc2l0aXZlX25lZ2F0aXZlRXBpc3Rhc2lzX2hpZ2hlck9yZGVydmFyaWFudHMucGRmIiwgcGxvdD1wLCB3aWR0aD03LjUsIGhlaWdodD0zKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9lcGlzdGFzaXMvUG9zaXRpdmVfbmVnYXRpdmVFcGlzdGFzaXNfaGlnaGVyT3JkZXJ2YXJpYW50cy5wbmciLCBwbG90PXAsIHdpZHRoPTcuNSwgaGVpZ2h0PTMpCmBgYAoKIyMgRXhhbXBsZXMgb2YgZXBpc3Rhc2lzCgpgYGB7cn0KZXBpc3RhdGljX3RhYmxlX3dpZGUgPC0gcGl2b3Rfd2lkZXIoZXBpc3RhdGljX3RhYmxlWyxjKCJDb25kaXRpb24iLCAiRXhjaGFuZ2VfdmFsdWVfYW5kX3JhdGlvX2luX1dUIiwgIkV4Y2hhbmdlIiwgIlZhcmlhbnQiLCAiVmFyaWFudF92YWx1ZSIsICJyYXRpb19XVF9oaWdoZXJPcmRlciIpXSwgbmFtZXNfZnJvbT1jKCJDb25kaXRpb24iKSwgdmFsdWVzX2Zyb209YygiVmFyaWFudF92YWx1ZSIsICJyYXRpb19XVF9oaWdoZXJPcmRlciIsICJFeGNoYW5nZV92YWx1ZV9hbmRfcmF0aW9faW5fV1QiKSkKZXBpc3RhdGljX3RhYmxlX3dpZGUgPC0gc3Vic2V0KGVwaXN0YXRpY190YWJsZV93aWRlLCBlcGlzdGF0aWNfdGFibGVfd2lkZSRyYXRpb19XVF9oaWdoZXJPcmRlcl9DTF9OMj4xLjUgJiBlcGlzdGF0aWNfdGFibGVfd2lkZSRyYXRpb19XVF9oaWdoZXJPcmRlcl9DTF9PMiA+IDEuNSAmIGVwaXN0YXRpY190YWJsZV93aWRlJHJhdGlvX1dUX2hpZ2hlck9yZGVyX0xEID4gMS41ICYgZXBpc3RhdGljX3RhYmxlX3dpZGUkVmFyaWFudF92YWx1ZV9DTF9OMiA+IDEuMCAmIGVwaXN0YXRpY190YWJsZV93aWRlJFZhcmlhbnRfdmFsdWVfQ0xfTzIgPiAxLjAgJiBlcGlzdGF0aWNfdGFibGVfd2lkZSRWYXJpYW50X3ZhbHVlX0xEID4gMS4wKQpucm93KGVwaXN0YXRpY190YWJsZV93aWRlKQplcGlzdGF0aWNfdGFibGVfd2lkZSRlcF9wcm9kdWN0IDwtIGVwaXN0YXRpY190YWJsZV93aWRlJHJhdGlvX1dUX2hpZ2hlck9yZGVyX0NMX04yICogZXBpc3RhdGljX3RhYmxlX3dpZGUkcmF0aW9fV1RfaGlnaGVyT3JkZXJfQ0xfTzIgKiBlcGlzdGF0aWNfdGFibGVfd2lkZSRyYXRpb19XVF9oaWdoZXJPcmRlcl9MRAplcGlzdGF0aWNfdGFibGVfd2lkZVtvcmRlcihlcGlzdGF0aWNfdGFibGVfd2lkZSRlcF9wcm9kdWN0LCBkZWNyZWFzaW5nPVRSVUUpLF0KYGBgCgpDaGVjayBhZ2FpbiBpZiBhbnkgb2YgdGhlICJnb29kLCByZWNvdmVyZWQiIHN0cmFpbnMgYXJlIGp1c3QgYW4gYXJ0ZWZhY3QgIQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJNMTU0SyIsICJNMTU0SyxIMjY1RSxRNDA2RSxTNDQ2VCIsICJIMjY1RSxRNDA2RSxTNDQ2VCIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIk0xNTRLIj0iIzg4Y2NlZWZmIiwgIk0xNTRLLEgyNjVFLFE0MDZFLFM0NDZUIj0iIzExNzczM2ZmIiwgIkgyNjVFLFE0MDZFLFM0NDZUIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgTTE1NEsiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzI2MUQiLCAiSzI2MUQsSDI2NUUsUTQwNkUsUzQ0NlQiLCAiSDI2NUUsUTQwNkUsUzQ0NlQiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJLMjYxRCI9IiM4OGNjZWVmZiIsICJLMjYxRCxIMjY1RSxRNDA2RSxTNDQ2VCI9IiMxMTc3MzNmZiIsICJIMjY1RSxRNDA2RSxTNDQ2VCI9IiNkZGNjNzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgaW52b2x2aW5nIEsyNjFEIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIlYzMzdBIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFYzMzdBLFE0MDZFLFM0NDZJIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFE0MDZFLFM0NDZJIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiVjMzN0EiPSIjODhjY2VlZmYiLCAiSDE0MVYsTTE1NEEsSzI2MUYsSDI2NUEsVjMzN0EsUTQwNkUsUzQ0NkkiPSIjMTE3NzMzZmYiLCAiSDE0MVYsTTE1NEEsSzI2MUYsSDI2NUEsUTQwNkUsUzQ0NkkiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGludm9sdmluZyBWMzM3QSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2VwaXN0YXRpYy1lZmZlY3RzLVYzMzdBLnBkZiIsIHBsb3Q9cCwgd2lkdGg9MjAsIGhlaWdodD0yMCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJRNDA2RSIsICJIMTQxVixNMTU0QSxLMjYxRixIMjY1QSxWMzM3QSxRNDA2RSxTNDQ2SSIsICJIMTQxVixNMTU0QSxLMjYxRixIMjY1QSxWMzM3QSxTNDQ2SSIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlE0MDZFIj0iIzg4Y2NlZWZmIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFYzMzdBLFE0MDZFLFM0NDZJIj0iIzExNzczM2ZmIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFYzMzdBLFM0NDZJIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgUTQwNkUiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9lcGlzdGF0aWMtZWZmZWN0cy1RNDA2RS5wZGYiLCBwbG90PXAsIHdpZHRoPTIwLCBoZWlnaHQ9MjApCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiUTQwNkUiLCAiSDE0MVYsTTE1NEEsUTQwNkUiLCAiSDE0MVYsTTE1NEEiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJRNDA2RSI9IiM4OGNjZWVmZiIsICJIMTQxVixNMTU0QSxRNDA2RSI9IiMxMTc3MzNmZiIsICJIMTQxVixNMTU0QSI9IiNkZGNjNzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgaW52b2x2aW5nIFE0MDZFIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIksyNjFBIiwgIksyNjFBLEgyNjVLLFE0MDZFLFM0NDZBIiwgIkgyNjVLLFE0MDZFLFM0NDZBIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSzI2MUEiPSIjODhjY2VlZmYiLCAiSzI2MUEsSDI2NUssUTQwNkUsUzQ0NkEiPSIjMTE3NzMzZmYiLCAiSDI2NUssUTQwNkUsUzQ0NkEiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgyNjVBIiwgIk0xNTRBLEsyNjFBLEgyNjVBLFE0MDZEIiwgIk0xNTRBLEsyNjFBLFE0MDZEIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSDI2NUEiPSIjODhjY2VlZmYiLCAiTTE1NEEsSzI2MUEsSDI2NUEsUTQwNkQiPSIjMTE3NzMzZmYiLCAiTTE1NEEsSzI2MUEsUTQwNkQiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGludm9sdmluZyBIMjY1QSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJTNDQ2VCIsICJNMTU0QSxLMjYxQSxRNDA2RCxTNDQ2VCIsICJNMTU0QSxLMjYxQSxRNDA2RCIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlM0NDZUIj0iIzg4Y2NlZWZmIiwgIk0xNTRBLEsyNjFBLFE0MDZELFM0NDZUIj0iIzExNzczM2ZmIiwgIk0xNTRBLEsyNjFBLFE0MDZEIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgUzQ0NlQiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzI2MUYiLCAiSzI2MUYsSDI2NUssUTQwNkQsUzQ0NkkiLCAiSDI2NUssUTQwNkQsUzQ0NkkiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJLMjYxRiI9IiM4OGNjZWVmZiIsICJLMjYxRixIMjY1SyxRNDA2RCxTNDQ2SSI9IiMxMTc3MzNmZiIsICJIMjY1SyxRNDA2RCxTNDQ2SSI9IiNkZGNjNzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgaW52b2x2aW5nIEsyNjFGIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgyNjVFIiwgIksyNjFELEgyNjVFLFE0MDZFLFM0NDZUIiwgIksyNjFELFE0MDZFLFM0NDZUIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSDI2NUUiPSIjODhjY2VlZmYiLCAiSzI2MUQsSDI2NUUsUTQwNkUsUzQ0NlQiPSIjMTE3NzMzZmYiLCAiSzI2MUQsUTQwNkUsUzQ0NlQiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGludm9sdmluZyBIMjY1RSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJLMjYxQSIsICJLMjYxQSxIMjY1SyxRNDA2RSxTNDQ2QSIsICJIMjY1SyxRNDA2RSxTNDQ2QSIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIksyNjFBIj0iIzg4Y2NlZWZmIiwgIksyNjFBLEgyNjVLLFE0MDZFLFM0NDZBIj0iIzExNzczM2ZmIiwgIkgyNjVLLFE0MDZFLFM0NDZBIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgSzI2MUEiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmBgYAoKIyBWYXJpYW50cyBmb3Igd2hpY2ggbXV0YW50IHN0cmFpbnMgZXhpc3QKCiJ2YXJpYW50IEgxNDFWLEsyNjFBLEgyNjVLLFE0MDZFLFM0NDZJIHdpdGggdGhlIGhpZ2hlc3QgZml0bmVzcyB2YWx1ZSBpbiB0aGUgbGlicmFyeSwgYnV0IHNoaXR0eSBhZGp1c3RlZCBwIHZhbHVlLCBpcyBIMTQxVixLMjYxQSxIMjY1SyxRNDA2RSxTNDQ2SSAobm9ybT0yLjE4LCBwLmFkaj0wLjEwNyksIGFsdGVybmF0aXZlIG9wdGlvbiBmb3Igc2hvd2luZyBlcGlzdGFzaXMgKGJlc2lkZXMgRCBhbmQgRSksIG5lZWRzIGNvbnRyb2wgc3RyYWlucyBIMTQxViwgSzI2MUEsIFE0MDZFLCBTNDQ2SSBhbmQgSDI2NUsgdG8gc2hvdyB0aGF0IEgyNjVLIGlzIGRldHJpbWVudGFsIHdoZW4gaW50cm9kdWNlZCBvbiBpdHMgb3duLCBiZW5lZmljaWFsIGluIGJhY2tncm91bmQgb2YgcXVhcmRydXBsZSBtdXRhbnQiCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgxNDFWLEsyNjFBLEgyNjVLLFE0MDZFLFM0NDZJIiwgIkgxNDFWLEsyNjFBLFE0MDZFLFM0NDZJIiwgIkgyNjVLIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGFuZCAnc3RyYWluIEEnIikKcApgYGAKCkgxNDFWLCBNMTU0QSwgSzI2MUEsIEgyNjVBLCBRNDA2RSwgUzQ0NlQJb3RoZXIgYWx0ZXJuYXRpdmUgb3B0aW9uIGZvciBzaG93aW5nIGVwaXN0YXNpcyAoYmVzaWRlcyBFKSwgdGhlcmUgc2hvdWxkIGFsc28gYmUgYSBjb3JyZXNwb25kaW5nIG11dGFudCB3aXRoIDUgZXhjaGFuZ2VzIChIMTQxViwgTTE1NEEsIEsyNjFBLCBRNDA2RSwgUzQ0NlQpIGFuZCB0aGVuIHRoZSBjb21wYXJpc29uIHRoYXQgSDI2NUEgaXMgZGV0cmltZW50YWwgb24gaXRzIG93biwgYnV0IGJlbmVmaWNpYWwgaW4gdGhlIGJhY2tncm91bmQgb2YgdGhlIG11dGFudCB3aXRoIDUgZXhjaGFnbmVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSDE0MVYsTTE1NEEsSzI2MUEsSDI2NUEsUTQwNkUsUzQ0NlQiLCAiSDE0MVYsTTE1NEEsSzI2MUEsUTQwNkUsUzQ0NlQiLCAiSDI2NUEiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgYW5kICdzdHJhaW4gRCciKQpwCmBgYAoKSDE0MVYsIEsyNjFFLCBIMjY1QSwgUzQ0NkEJImZpdHRlc3QgY29tYmluYXRvcmlhbCBtdXRhbnQgdmFyaWFudCB3aXRoIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBoYXMgZm91cgphbWlubyBhY2lkIGV4Y2hhbmdlczogSDE0MVYsSzI2MUUsSDI2NUEsUzQ0NkEgKG5vcm09MS41NiwgcC5hZGo9MC4wMjU5KQosIG1haW4gbGluZSBGaWd1cmUgc2hvd2luZyBlcGlzdGFzaXMsIHRoZXJlIHNob3VsZCBhbHNvIGJlIGEgY29ycmVzcG9uZGluZyB0cmlwbGUgbXV0YW50IChIMTQxViwgSzI2MUUsIFM0NDZBKSBzaG93aW5nIHRoYXQgSDI2NUEgb24gaXRzIG93biBpcyBkZXRyaW1lbnRhbCwgaW4gYmFja2dyb3VuZCBvZiBIMTQxViwgSzI2MUUsIFM0NDZBIGJlbmVmaWNpYWwiCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgxNDFWLEsyNjFFLEgyNjVBLFM0NDZBIiwgIkgxNDFWLEsyNjFFLFM0NDZBIiwgIkgyNjVBIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGFuZCAnc3RyYWluIEUnIikKcApgYGAKClZhcmlhbnQgdW5kZXJseWluZyB0aGUgc3VnZ2VzdGVkIGhpZ2hlci1vcmRlciB2YXJpYW50czogSDE0MVYsIEsyNjFBLCBIMjY1QSwgUTQwNkUsIFM0NDZUCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiVzEyOEkiPSIjODhjY2VlZmYiLCAiSDE0MVYsSzI2MUEsSDI2NUEsUTQwNkUsUzQ0NlQiPSIjMTE3NzMzZmYiLCAiSzM2MEkiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgxNDFWLEsyNjFBLEgyNjVBLFE0MDZFLFM0NDZUIiwgIlcxMjhJIiwgIkszNjBJIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZhcmlhbnQgdW5kZXJseWluZyBCLCBDLCBCZml4IGFuZCBzaW5nbGUgZXhjaGFuZ2VzIGludHJvZHVjZWQgaW4gdGhlc2UgdmFyaWFudHMiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi92YXJpYW50X3VuZGVybHlpbmdfQi1DLUJmaXgucGRmIiwgcGxvdD1wLCB3aWR0aD0yMCwgaGVpZ2h0PTIwKQpgYGAKClcxMjhJCXNpbmdsZSBleGNoYW5nZSB3aXRoIGhpZ2ggZml0bmVzcyB2YWx1ZSwgSDM1OFMJc2luZ2xlIGV4Y2hhbmdlIHdpdGggaGlnaCBmaXRuZXNzIHZhbHVlLCBLMzYwSQlzaW5nbGUgZXhjaGFuZ2Ugd2l0aCBoaWdoIGZpdG5lc3MgdmFsdWUKCjwhLS0gU2luZ2xlIGFtaW5vIGFjaWQgZXhjaGFuZ2UgdmFyaWFudHMgd2l0aCBhIGhpZ2ggbm9ybWFsaXplZCBmaXRuZXNzIHNjb3JlIGFuZCBhIGxvdyBhZGp1c3RlZCBwIHZhbHVlIGFyZSBXMTI4SSAobm9ybT0xLjY3LCBwLmFkaj0wLjAwMiksIEgzNThTIChub3JtPTEuNywgcC5hZGo9MC4wMDAwMSkgYW5kIEszNjBJIChub3JtPTEuNjYsIHAuYWRqPTAuMDAwMDAwOTM3KS4gLS0+CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIlcxMjhJIiwgIkgzNThTIiwgIkszNjBJIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlNpbmdsZSBleGNoYW5nZXMgYWRkZWQgY2xvbmVkIGFzIHNpbmdsZSBtdXRhbnQgc3RyYWlucyIpCnAKYGBgCgpIaWdoZXIgb3JkZXIgdmFyaWFudCB3aXRoIFY5OEQsIEYxMDRDLCBXMTI4SywgUTE0MkQsIEsyMzNJLCBWMjcwTCwgTTM0NUksIEgzNThTCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIlY5OEQiLCAiRjEwNEMiLCAiVzEyOEsiLCAiUTE0MkQiLCAiSzIzM0kiLCAiVjI3MEwiLCAiTTM0NUkiLCAiSDM1OFMiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iU2luZ2xlIGV4Y2hhbmdlcyB1bmRlcmx5aW5nIGhpZ2hlciBvcmRlciB2YXJpYW50ICgnTTgnKSIpCnAKYGBgCgojIE90aGVyIGFuYWx5c2VzIGZvciBjb21iaW5hdG9yaWFsIGxpYnJhcnkKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpzaW5nbGVfbXV0c19jb21iaW5hdG9yaWFsIDwtIHBpdm90X3dpZGVyKHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnk9PSJjb21iaUFORHNhdHVyIilbLGNvbHVtbnNdKSwgbmFtZXNfZnJvbT1jb25kaXRpb24sIHZhbHVlc19mcm9tPWMobm9ybSwgcF9maXRfYWRqX1dUKSkKc2luZ2xlX211dHNfY29tYmluYXRvcmlhbCRub3JtX211bHRpcGx5IDwtIHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9OMiAqIHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9PMiAqIHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9MRAp3cml0ZS5jc3Yoc2luZ2xlX211dHNfY29tYmluYXRvcmlhbCwgZmlsZT0iLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3NpbmdsZV9zaXRlX2V4Y2hhbmdlc19jb21iaW5hdG9yaWFsTGlicmFyeS5jc3YiKQpzaW5nbGVfbXV0c19jb21iaW5hdG9yaWFsW29yZGVyKHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9tdWx0aXBseSwgZGVjcmVhc2luZz1UUlVFKSxjKDEsNCw1LDYsMTAsMiwzLDcsOCw5KV0KYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KY29tYmlfc2luZ2xlX3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiV1QiLCAiSDI2NUsiLCAiSDI2NVIiLCAiVjMzN0EiLCAiVjMzN1MiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKGNvbWJpX3NpbmdsZV9zdWJzZXQpICsgbGFicyh0aXRsZT0iTW9zdCBkZXRyaW1lbnRhbCBzaW5nbGUgdmFyaWFudHMgaW4gY29tYmluYXRvcmlhbCBsaWJyYXJ5IikKcApgYGAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CmZvcihuX211dCBpbiAyOjcpewogIHByaW50KG5fbXV0KQogIG11dHNfY29tYmluYXRvcmlhbCA8LSBwaXZvdF93aWRlcih1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJG51bWJlcl9tdXRzPT1uX211dClbLGNvbHVtbnNdKSwgbmFtZXNfZnJvbT1jb25kaXRpb24sIHZhbHVlc19mcm9tPWMobm9ybSwgcF9maXRfYWRqX1dUKSkKICBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9tdWx0aXBseSA8LSBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9OMiAqIG11dHNfY29tYmluYXRvcmlhbCRub3JtX0NMX08yICogbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fTEQKICBwcmludCgiU29ydGVkIGFjY29yZGluZyB0byBwcm9kdWN0IG9mIGRpZmZlcmVudCBub3JtZWQgZml0bmVzcyB2YWx1ZXMiKQogIHByaW50KGhlYWQobXV0c19jb21iaW5hdG9yaWFsW29yZGVyKG11dHNfY29tYmluYXRvcmlhbCRub3JtX211bHRpcGx5LCBkZWNyZWFzaW5nPVRSVUUpLGMoMSw0LDUsNiwxMCwyLDMsNyw4LDkpXSkpCiAgbXV0c19jb21iaW5hdG9yaWFsIDwtIHN1YnNldChtdXRzX2NvbWJpbmF0b3JpYWwsIG11dHNfY29tYmluYXRvcmlhbCRub3JtX0NMX04yID4gMS4wICYgbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fQ0xfTzIgPiAxLjAgJiBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9MRCA+IDEuMCkKICBtdXRzX2NvbWJpbmF0b3JpYWwgPC0gc3Vic2V0KG11dHNfY29tYmluYXRvcmlhbCwgKG11dHNfY29tYmluYXRvcmlhbCRwX2ZpdF9hZGpfV1RfQ0xfTjIgPCAwLjA1IHwgbXV0c19jb21iaW5hdG9yaWFsJHBfZml0X2Fkal9XVF9DTF9PMiA8IDAuMDUgfCBtdXRzX2NvbWJpbmF0b3JpYWwkcF9maXRfYWRqX1dUX0xEIDwgMC4wNSkgJiBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9OMiA+IDEuMCAmIG11dHNfY29tYmluYXRvcmlhbCRub3JtX0NMX08yID4gMS4wICYgbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fTEQgPiAxLjApIAogIHByaW50KCJPbmx5IHNlbGVjdGluZyBmb3IgdmFyaWFudHMgd2l0aCBhIG5vcm1lZCBmaXRuZXNzIHZhbHVlIGFib3ZlIDEgaW4gYWxsIGNvbmRpdGlvbnMgYW5kIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSB0byB0aGUgYmFzZSB2YXJpYW50IGluIGF0IGxlYXN0IG9uZSBvZiB0aGUgY29uZGl0aW9ucyIpCiAgcHJpbnQoaGVhZChtdXRzX2NvbWJpbmF0b3JpYWxbb3JkZXIobXV0c19jb21iaW5hdG9yaWFsJG5vcm1fbXVsdGlwbHksIGRlY3JlYXNpbmc9VFJVRSksYygxLDQsNSw2LDEwLDIsMyw3LDgsOSldKSkKfQoKYGBgCiMgVHJ1bmNhdGVkIHZhcmlhbnRzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KdHJ1bmNhdGVkX3ZhcmlhbnRzIDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRjYXRlZ29yeSA9PSJub3RFeHBlY3RlZCIpCmxlbmd0aCh1bmlxdWUodHJ1bmNhdGVkX3ZhcmlhbnRzJHNnUk5BX3RhcmdldCkpCnRydW5jYXRlZF93aWRlIDwtIHBpdm90X3dpZGVyKHVuaXF1ZSh0cnVuY2F0ZWRfdmFyaWFudHNbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIildKSwgdmFsdWVzX2Zyb20gPWMobm9ybSxwX2ZpdF9hZGpfV1QpLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKdHJ1bmNhdGVkX3dpZGUkbm9ybV9wcm9kdWN0IDwtIHRydW5jYXRlZF93aWRlJG5vcm1fQ0xfTjIgKiB0cnVuY2F0ZWRfd2lkZSRub3JtX0NMX08yICogdHJ1bmNhdGVkX3dpZGUkbm9ybV9MRAp0cnVuY2F0ZWRfd2lkZVtvcmRlcih0cnVuY2F0ZWRfd2lkZSRub3JtX3Byb2R1Y3QsIGRlY3JlYXNpbmc9VFJVRSksXQp0cnVuY2F0ZWRfc3Vic2V0IDwtIGNvbWJpX3NpbmdsZV9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkczOTVWLFNUT1AzOTUiLCAiRjQwNVMsUTQwNlIsTjQwN1QsU1RPUDQwNyIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp0cnVuY2F0ZWRfc3Vic2V0JHNnUk5BX3RhcmdldCA8LSBmYWN0b3IodHJ1bmNhdGVkX3N1YnNldCRzZ1JOQV90YXJnZXQsIGxldmVscz1jKCJHMzk1VixTVE9QMzk1IiwgIkY0MDVTLFE0MDZSLE40MDdULFNUT1A0MDciLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiRzM5NVYsU1RPUDM5NSI9IiM0NGFhOTlmZiIsICJGNDA1UyxRNDA2UixONDA3VCxTVE9QNDA3Ij0iI2FhNDQ5OWZmIiwgIldUIj0iYmxhY2siLCAiSzIxNFIiPSJkYXJrZ3JheSIpCmRpZmZlcmVudF9saW5ldHlwZXNfc2V0IDwtIGMoIkczOTVWLFNUT1AzOTUiPTQ0LCAiRjQwNVMsUTQwNlIsTjQwN1QsU1RPUDQwNyI9MTM0MywgIldUIj0ic29saWQiLCAiSzIxNFIiPSJsb25nZGFzaCIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMih0cnVuY2F0ZWRfc3Vic2V0KSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2xpbmV0eXBlc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi90cnVuY2F0ZWRfdmFyaWFudHNfcGVyZm9ybWluZ193ZWxsLnBkZiIsIHBsb3Q9cCwgaGVpZ2h0PTEuNzUsIHdpZHRoPTYsIHVuaXRzPSJpbiIpCnAKYGBgCgojIFByaW50IHRhYmxlcyB3aXRoIG11dGF0aW9uYWwgZWZmZWN0IG9mIHNpbmdsZSBhbWlubyBhY2lkIGV4Y2hhbmdlcwoKYGBge3J9CnNpbmdsZV9tdXRzIDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkbnVtYmVyX211dHM9PTEgJiBmaXRuZXNzX2RhdGEkY29uZGl0aW9uPT0iQ0xfTjIiKVssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAic2RfZml0bmVzcyIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIildKQp3cml0ZV9jc3Yoc2luZ2xlX211dHMsICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvaGVhdE1hcC9zaW5nbGVfbXV0c19DTE4yLmNzdiIpCgpzaW5nbGVfbXV0cyA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJG51bWJlcl9tdXRzPT0xICYgZml0bmVzc19kYXRhJGNvbmRpdGlvbj09IkNMX08yIilbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgInNkX2ZpdG5lc3MiLCAicF9maXRfYWRqX1dUIiwgIm51bV9iYXJjb2RlcyIpXSkKd3JpdGVfY3N2KHNpbmdsZV9tdXRzLCAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL2hlYXRNYXAvc2luZ2xlX211dHNfQ0xPMi5jc3YiKQoKc2luZ2xlX211dHMgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRudW1iZXJfbXV0cz09MSAmIGZpdG5lc3NfZGF0YSRjb25kaXRpb249PSJMRCIpWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJzZF9maXRuZXNzIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pCndyaXRlX2NzdihzaW5nbGVfbXV0cywgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9oZWF0TWFwL3NpbmdsZV9tdXRzX0xELmNzdiIpCmBgYAoKIyBDcmVhdGUgZmlsZXMgZm9yIFByb3RlaW5OUFQKCkNvbXBhcmUgaHR0cHM6Ly9naXRodWIuY29tL09BVE1MLU1hcmtzbGFiL1Byb3RlaW5OUFQuIEV4cGVjdHMgYSAuY3N2IGZpbGUuICJBdCBhIG1pbmltdW0gdGhpcyBmaWxlIHJlcXVpcmVzIDIgZmllbGRzOiBtdXRhdGVkX3NlcXVlbmNlIChmdWxsIHNlcXVlbmNlIG9mIGFtaW5vIGFjaWRzKSBhbmQgRE1TX3Njb3JlIChhc3NheSBtZWFzdXJlbWVudCkuIElmIG5vIGZvbGQgdmFyaWFibGUgaXMgaW5jbHVkZWQgaW4gdGhlIGFzc2F5IGZpbGUsIHRoZSBwaXBlbGluZSBzY3JpcHQgd2lsbCBhdXRvbWF0aWNhbGx5IGNyZWF0ZSBhIGZvbGRfcmFuZG9tXzUgdmFyaWFibGUsIGFzc2lnbmluZyBlYWNoIG11dGFudCB0byBmb2xkcyAwLTQgYXQgcmFuZG9tLiBZb3UgbWF5IGFsc28gdXNlIHlvdXIgb3duIGNyb3NzLXZhbGlkYXRpb24gc2NoZW1lIChlZy4sIGFzc2lnbiBhbGwgdHJhaW5pbmcgc2VxdWVuY2VzIHRvIGZvbGQgMCwgYWxsIHRlc3Qgc2VxdWVuY2VzIHRvIGZvbGQgMSkuIFRvIHRoYXQgZW5kLCB5b3Ugb25seSBuZWVkIHRvIHBhc3MgdG8gdGhlIHBpcGVsaW5lIHNjcmlwdCB0aGUgbmFtZSBvZiB0aGF0IGZvbGQgdmFyaWFibGUgdmlhIHRoZSBmb2xkX3ZhcmlhYmxlX25hbWUgYXJndW1lbnQgYW5kIHNwZWNpZnkgdGhlIGluZGV4IG9mIHRoZSB0ZXN0IGZvbGQgdmlhIHRoZSB0ZXN0X2ZvbGRfaW5kZXggYXJndW1lbnQgKGlmIHRlc3RfZm9sZF9pbmRleCBpcyBub3QgcGFzc2VkIGFzIGFyZ3VtZW50LCB0aGUgc2NyaXB0IHdpbGwgYXV0b21hdGljYWxseSBwZXJmb3JtIGEgZnVsbCBjcm9zcy12YWxpZGF0aW9uLCByb3RhdGluZyB0aGUgdGVzdCBmb2xkIGluZGV4IGF0IGVhY2ggaXRlcmF0aW9uKS4iCgpDcmVhdGUgYSBmaWxlIGZvciAKYSkgcHJlZGljdGluZyBoaWdoZXItb3JkZXIgY29tYmluYXRpb25zCi0gc2VwYXJhdGUgYW1pbm8gYWNpZCBwb3NpdGlvbnMgaW50byA1IGZvbGRzIC0tPiBhZGQgZm9sZHMgdXNpbmcgcHl0aG9uCi0gdGhyZWUgZmlsZXMgZm9yIGVhY2ggY29uZGl0aW9uCgpVc2UgcHl0aG9uIHNjcmlwdCBzaW5nbGVfbXV0c190cmFpbl9mb3JfY29tYmkucHkgdG8gYXNzaWduIGZvbGRzIGZvciB0cmFpbmluZyBldGMuLCBvdXRwdXQgc2F0dXJhdGlvbmFsX3Byb3RlaW5OUFRfd2l0aF9mb2xkQ2hhbmdlLmNzdgoKUmVnYXJkaW5nIGZvbGRzOiAiV2UgZGV2ZWxvcCAzIGRpc3RpbmN0IGNyb3NzLXZhbGlkYXRpb24gc2NoZW1lcyB0byBhc3Nlc3MgdGhlIGFiaWxpdHkgb2YgZWFjaCBtb2RlbCB0byBleHRyYXBvbGF0ZSB0byBwb3NpdGlvbnMgbm90IGVuY291bnRlcmVkIGR1cmluZyB0cmFpbmluZy4gSW4gdGhlIFJhbmRvbSBzY2hlbWUsIGNvbW1vbmx5LXVzZWQgaW4gb3RoZXIgc3VwZXJ2aXNlZCBmaXRuZXNzIHByZWRpY3Rpb24gYmVuY2htYXJrcyBbUmFvIGV0IGFsLiwgMjAxOSwgRGFsbGFnbyBldCBhbC4sIDIwMjJdLCBlYWNoIG11dGF0aW9uIGlzIHJhbmRvbWx5IGFsbG9jYXRlZCB0byBvbmUgb2YgZml2ZSBkaXN0aW5jdCBmb2xkcy4gSW4gdGhlIENvbnRpZ3VvdXMgc2NoZW1lLCB0aGUgc2VxdWVuY2UgaXMgc3BsaXQgaW50byBmaXZlIGNvbnRpZ3VvdXMgc2VnbWVudHMgYWxvbmcgaXRzIGxlbmd0aCwgd2l0aCBtdXRhdGlvbnMgYXNzaWduZWQgdG8gZWFjaCBzZWdtZW50IGJhc2VkIG9uIHRoZSBwb3NpdGlvbiB0aGV5IG9jY3VyIGluIHRoZSBzZXF1ZW5jZS4gTGFzdGx5LCB0aGUgTW9kdWxvIHNjaGVtZSB1c2VzIHRoZSBtb2R1bG8gb3BlcmF0b3IgdG8gYXNzaWduIG11dGF0ZWQgcG9zaXRpb25zIHRvIGVhY2ggZm9sZC4gRm9yIGV4YW1wbGUsIHBvc2l0aW9uIDEgaXMgYXNzaWduZWQgdG8gZm9sZCAxLCBwb3NpdGlvbiAyIHRvIGZvbGQgMiwgYW5kIHNvIG9uLCBsb29waW5nIGJhY2sgdG8gZm9sZCAxIGF0IHBvc2l0aW9uIDYuIFRoaXMgcGF0dGVybiBjb250aW51ZXMgdGhyb3VnaG91dCB0aGUgc2VxdWVuY2UuIFdlIG5vdGUgdGhhdCB0aGVyZSBpcyBubyBpbmhlcmVudCBpc3N1ZSB3aXRoIHVzaW5nIGEgUmFuZG9tIGNyb3NzLXZhbGlkYXRpb24gc2NoZW1lIHRvIGVzdGltYXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBwcmVkaWN0aXZlIG1vZGVscy4gSG93ZXZlciwgdGhlIGNvbmNsdXNpb25zIGRyYXduIGFuZCB0aGUgZ2VuZXJhbGl6YWJpbGl0eSBjbGFpbXMgYmFzZWQgb24gaXQgcmVxdWlyZSBjYXJlZnVsIGNvbnNpZGVyYXRpb24uIiAtLT4gdXNlIG1vZHVsbyBzY2hlbWUgZm9yIGFzc2lnbmluZyBmb2xkcyB0byBzYXR1cmF0aW9uYWwgbGlicmFyeQoKYikgcHJlZGljdGluZyBtb3JlIGNvbWJpbmF0aW9ucwotIGhvdyB0byBhc3NpZ24gZm9sZHM/IHRha2UgbW9kdWxvIGZvbGRzIGZyb20gc2F0dXJhdGlvbmFsLCBjb21iaW5lIGNvbWJpbmF0b3JpYWwgcGFydCBhbmQgc2F0dXJhdGlvbmFsIHBhcnQgaW4gdGhlIGVuZAoKCmBgYHtyfQpzYXR1cmF0aW9uYWxfZm9yX3Byb3RlaW5OUFQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRudW1iZXJfbXV0cz09MSlbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImJhc2VBQSIsICJjb25kaXRpb24iKV0pCnNhdHVyYXRpb25hbF9mb3JfcHJvdGVpbk5QVCA8LSBwaXZvdF93aWRlcihzYXR1cmF0aW9uYWxfZm9yX3Byb3RlaW5OUFQsIG5hbWVzX2Zyb209Y29uZGl0aW9uLCB2YWx1ZXNfZnJvbT1ub3JtKQp3cml0ZV9jc3Yoc2F0dXJhdGlvbmFsX2Zvcl9wcm90ZWluTlBULCAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL2Nzdl9mb3JfcHJvdGVpbk5QVC9zYXR1cmF0aW9uYWxfZm9yX3Byb3RlaW5OUFQuY3N2IikKCmFsbF9mb3JfcHJvdGVpbk5QVCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJG51bWJlcl9tdXRzPjEpWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iKV0pCmFsbF9mb3JfcHJvdGVpbk5QVCA8LSBwaXZvdF93aWRlcihhbGxfZm9yX3Byb3RlaW5OUFQsIG5hbWVzX2Zyb209Y29uZGl0aW9uLCB2YWx1ZXNfZnJvbT1ub3JtKQp3cml0ZV90c3YoYWxsX2Zvcl9wcm90ZWluTlBULCAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL2Nzdl9mb3JfcHJvdGVpbk5QVC9jb21iaW5hdG9yaWFsX2Zvcl9wcm90ZWluTlBULnRzdiIpCmBgYAoKIyBDcmVhdGUgZmlsZSBmb3IgU3VwcGxlbWVudAoKYGBge3J9CmNvbHVtbnNfZm9yX3RhYmxlIDwtIGMoInNnUk5BX3RhcmdldCIsICJudW1fYmFyY29kZXMiLCAibnVtYmVyX211dHMiLCAibm9ybSIsICJjb25kaXRpb24iLCAicF9maXRfYWRqX1dUIiwgIkVWY291cF9wcmVkaWN0IiwgIkRlZXBTZXFfcHJlZGljdCIsICJNU0FfVHJhbnNmb3JtIiwgInByb3RlaW5OUFRfcHJlZGljdCIsICJhZGRpdGl2ZV9zY29yZSIsICJjb25zZXJ2YXRpb25TY29yZSIsICJhYnNTQVMiLCAicmVsU0FTIiwgImRpbWVyIikKCmxvbmdfZm9ybWF0IDwtIHVuaXF1ZShmaXRuZXNzX2RhdGFbLGNvbHVtbnNfZm9yX3RhYmxlXSkKd3JpdGVfY3N2KGxvbmdfZm9ybWF0LCAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL1N1cHBUYWJsZV9hbGxfZml0bmVzc192YWx1ZXNfbG9uZy5jc3YiKQoKd2lkZV9mb3JtYXQgPC0gcGl2b3Rfd2lkZXIobG9uZ19mb3JtYXQsIG5hbWVzX2Zyb209Y29uZGl0aW9uLCB2YWx1ZXNfZnJvbT1jKG5vcm0sIHBfZml0X2Fkal9XVCkpCndyaXRlX2Nzdih3aWRlX2Zvcm1hdCwgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9TdXBwVGFibGVfYWxsX2ZpdG5lc3NfdmFsdWVzX3dpZGUuY3N2IikKYGBgCgojIFNlc3Npb24gSW5mbyB7LnVubnVtYmVyZWR9CgpgYGB7ciBzZXNzaW9uSW5mbywgZWNobz1GQUxTRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==